Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save harrysbaraini/4ad7a998dd423517da9815d5af6decfb to your computer and use it in GitHub Desktop.
Save harrysbaraini/4ad7a998dd423517da9815d5af6decfb to your computer and use it in GitHub Desktop.
Laravel API Resources with permissions and links
<?php
namespace App\Http\Resources;
use App\Http\Handlers\Admin\UploadMediaFile;
use App\Http\Resources\Support\ResourceLink;
use App\Policies\EventPolicy;
use Illuminate\Support\Facades\Auth;
class EventResource extends ItemResource
{
/**
* Prepare the array of attributes.
*
* @return array
*/
protected function properties(): array
{
return [
'slug' => $this->slug,
'title' => $this->title,
'status' => $this->status,
'statusLabel' => $this->status_label,
'summary' => $this->summary,
'content' => $this->content,
'date' => $this->date,
'closingDate' => $this->closing_date,
'sales' => Auth::guard()->user()->isAdmin() ? [
'ticketsSold' => (int)$this->tickets_sold,
'totalSales' => (int)$this->total_sales,
'gatewayFee' => (int)$this->gateway_fee,
'organiserProfit' => (int)$this->organiser_profit,
'platformProfit' => (int)$this->platform_profit,
] : null,
'flags' => [
'isPublished' => $this->isPublished(),
'isOpenToEntries' => $this->isOpenToEntries(),
'isViewableOnFrontPage' => $this->isViewableOnFrontPage(),
],
'metadata' => [
'receiptNotes' => $this->metadata['receipt_notes'],
],
];
}
/**
* @return array
*/
protected function relationships(): array
{
return [
'logo' => new FileResource($this->getLogo()),
'banner' => new FileResource($this->getBanner()),
'sponsors' => new FileResourceCollection($this->getSponsors()),
'options' => new EventOptionResourceCollection($this->options),
'address' => new AddressResource($this->address),
'organisers' => new OrganiserResourceCollection($this->organisers),
];
}
/**
* Prepare the array of links.
*
* @return ResourceLink
*/
protected function links(): ResourceLink
{
return ResourceLink::make()
->putIf($this->allows('view', $this->resource), 'self', route('backend.events.show', $this->resource))
->putIf($this->allows('update', $this->resource), 'edit', route('backend.events.edit', $this->resource))
->putIf($this->allows('update', $this->resource), 'updateBasicInformation', route('backend.events.update.basic-information', $this->resource))
->putIf($this->allows('update', $this->resource), 'updateContent', route('backend.events.update.content', $this->resource))
->putIf($this->allows('update', $this->resource), 'updateDates', route('backend.events.update.dates', $this->resource))
->putIf($this->allows('update', $this->resource), 'updateImages', route('backend.events.update.images', $this->resource))
->putIf($this->allows('update', $this->resource), 'updateAddress', route('backend.events.update.address', $this->resource))
->putIf($this->allows('update', $this->resource), 'updatePrices', route('backend.events.update.prices', $this->resource))
->putIf($this->allows('update', $this->resource), 'updateSponsors', route('backend.events.update.sponsors', $this->resource))
->putIf($this->allows('update', $this->resource), 'updateOthers', route('backend.events.update.others', $this->resource))
->putIf($this->allows('preview', $this->resource), 'preview', route('backend.events.preview', $this->resource))
->putIf($this->allows('export', $this->resource), 'exportSheet', route('backend.events.export.sheet', $this->resource))
->putIf($this->allows('update', $this->resource), 'uploadMedia', handler(UploadMediaFile::class))
->putIf($this->isViewableOnFrontPage(), 'frontpage', ['event.show', $this->resource->slug]);
}
/**
* Prepare the array of permissions.
*
* @return PolicyResource
*/
protected function permissions(): PolicyResource
{
return new PolicyResource(new EventPolicy, $this->resource);
}
}
<?php
namespace App\Http\Resources;
use App\Http\Resources\Support\ResourceLink;
use App\Models\Event;
use App\Policies\EventPolicy;
use Illuminate\Support\Facades\Gate;
class EventResourceCollection extends ItemResourceCollection
{
/**
* Prepare the array of links.
*
* @return ResourceLink
*/
protected function links(): ResourceLink
{
return ResourceLink::make()
->putIf(Gate::allows('list', Event::class), 'self', route('backend.events.index'))
->putIf(Gate::allows('create', Event::class), 'create', route('backend.events.create'));
}
/**
* Prepare the array of permissions.
*
* @return PolicyResource
*/
protected function permissions(): PolicyResource
{
return new PolicyResource(new EventPolicy, Event::class);
}
}
<?php
namespace App\Http\Resources;
use App\Http\Resources\Support\ResourceLink;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Str;
abstract class ItemResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
* @throws \ReflectionException
*/
public function toArray($request)
{
return [
'type' => $this->type(),
'id' => $this->id,
'attributes' => $this->properties(),
'links' => $this->links()->toArray(),
'permissions' => $this->permissions(),
'relationships' => $this->relationships(),
];
}
/**
* Prepare the array of attributes.
*
* @return array
*/
abstract protected function properties(): array;
/**
* Prepare the array of links.
*
* @return ResourceLink
*/
abstract protected function links(): ResourceLink;
/**
* Prepare the array of permissions.
*
* @return PolicyResource
*/
abstract protected function permissions(): PolicyResource;
/**
* Prepare the array of relationships.
*
* @return array
*/
protected function relationships(): array
{
return [];
}
/**
* Guess the type of the resource.
*
* @return string
* @throws \ReflectionException
*/
protected function type(): string
{
$name = Str::replaceLast('Resource', '', (new \ReflectionClass($this))->getShortName());
return Str::snake(Str::plural($name));
}
protected function allows(...$parameters): bool
{
return Gate::allows(...$parameters);
}
}
<?php
namespace App\Http\Resources;
use App\Http\Resources\Support\ResourceLink;
use Illuminate\Http\Resources\Json\ResourceCollection;
use Illuminate\Support\Facades\Gate;
abstract class ItemResourceCollection extends ResourceCollection
{
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
public function with($request)
{
return [
'links' => $this->links()->toArray(),
'permissions' => $this->permissions(),
];
}
/**
* Prepare the array of links.
*
* @return ResourceLink
*/
abstract protected function links(): ResourceLink;
/**
* Prepare the array of permissions.
*
* @return PolicyResource
*/
abstract protected function permissions(): PolicyResource;
protected function allows(...$parameters): bool
{
return Gate::allows(...$parameters);
}
}
<?php
namespace App\Http\Resources;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
class PolicyResource extends JsonResource
{
protected $model;
protected $ignoreMethods = [];
protected $permissions = [];
/**
* Create a new resource instance.
*
* @param mixed $resource
* @param $model
*/
public function __construct($resource = null, $model = null)
{
$this->resource = $resource;
$this->model = $model;
}
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
* @throws \ReflectionException
*/
public function toArray($request)
{
if (is_null($this->resource)) {
return [];
}
$methods = (new \ReflectionClass($this->resource))->getMethods(\ReflectionMethod::IS_PUBLIC);
$policyPermissions = Collection::make($methods)
->except($this->ignoreMethods)
->filter(function (\ReflectionMethod $method) {
if ($this->model instanceof Model) {
return $method->getNumberOfParameters() > 1;
}
return $method->getNumberOfParameters() === 1;
})
->mapWithKeys(function (\ReflectionMethod $method) use ($request) {
return [
$method->name => $request->user('backend')->can($method->name, $this->model),
];
});
return $policyPermissions->merge($this->permissions)->toArray();
}
public function push(string $key, bool $value)
{
Arr::set($this->permissions, $key, $value);
return $this;
}
public function ignoreMethods(array $methods)
{
$this->ignoreMethods = $methods;
return $this;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment