Find this useful? Enter your email to receive occasional updates for securing PHP code.
Signing you up...
Thank you for signing up!
PHP Decode
<?php namespace Orion\Concerns; use Exception; use Illuminate\Contracts\Pagination\Pagin..
Decoded Output download
<?php
namespace Orion\Concerns;
use Exception;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Arr;
use InvalidArgumentException;
use Orion\Http\Requests\Request;
use Orion\Http\Resources\CollectionResource;
use Orion\Http\Resources\Resource;
trait HandlesRelationStandardOperations
{
/**
* Fetch the list of relation resources.
*
* @param Request $request
* @param int|string $parentKey
* @return CollectionResource
*/
public function index(Request $request, $parentKey)
{
$requestedRelations = $this->relationsResolver->requestedRelations($request);
$parentQuery = $this->buildIndexParentFetchQuery($request, $parentKey);
$parentEntity = $this->runIndexParentFetchQuery($request, $parentQuery, $parentKey);
$this->authorize($this->resolveAbility('index'), [$this->resolveResourceModelClass(), $parentEntity]);
$beforeHookResult = $this->beforeIndex($request, $parentEntity);
if ($this->hookResponds($beforeHookResult)) {
return $beforeHookResult;
}
$this->authorize($this->resolveAbility('show'), $parentEntity);
$query = $this->buildIndexFetchQuery($request, $parentEntity, $requestedRelations);
$entities = $this->runIndexFetchQuery(
$request,
$query,
$parentEntity,
$this->paginator->resolvePaginationLimit($request)
);
($entities instanceof Paginator ? $entities->getCollection() : $entities)->transform(
function ($entity) {
$entity = $this->cleanupEntity($entity);
if (count($this->getPivotJson())) {
$entity = $this->castPivotJsonFields($entity);
}
return $entity;
}
);
$afterHookResult = $this->afterIndex($request, $parentEntity, $entities);
if ($this->hookResponds($afterHookResult)) {
return $afterHookResult;
}
$this->relationsResolver->guardRelationsForCollection(
$entities instanceof Paginator ? $entities->getCollection() : $entities,
$requestedRelations
);
return $this->collectionResponse($entities);
}
/**
* The hooks is executed before fetching the list of relation resources.
*
* @param Request $request
* @param Model $parentEntity
* @return mixed
*/
protected function beforeIndex(Request $request, Model $parentEntity)
{
return null;
}
/**
* Builds Eloquent query for fetching parent entity in index method.
*
* @param Request $request
* @param string|int $parentKey
* @return Builder
*/
protected function buildIndexParentFetchQuery(Request $request, $parentKey): Builder
{
return $this->buildParentFetchQuery($request, $parentKey);
}
/**
* Builds Eloquent query for fetching parent entity.
*
* @param Request $request
* @param string|int $parentKey
* @return Builder
*/
protected function buildParentFetchQuery(Request $request, $parentKey): Builder
{
return $this->queryBuilder->buildQuery($this->newModelQuery(), $request);
}
/**
* Runs the given query for fetching parent entity in index method.
*
* @param Request $request
* @param Builder $query
* @param string|int $parentKey
* @return Model
*/
protected function runIndexParentFetchQuery(Request $request, Builder $query, $parentKey): Model
{
return $this->runParentFetchQuery($request, $query, $parentKey);
}
/**
* Runs the given query for fetching parent entity.
*
* @param Request $request
* @param Builder $query
* @param string|int $parentKey
* @return Model
*/
protected function runParentFetchQuery(Request $request, Builder $query, $parentKey): Model
{
return $query->where($this->resolveQualifiedParentKeyName(), $parentKey)->firstOrFail();
}
/**
* Builds Eloquent query for fetching relation entities in index method.
*
* @param Request $request
* @param Model $parentEntity
* @param array $requestedRelations
* @return Relation
*/
protected function buildIndexFetchQuery(Request $request, Model $parentEntity, array $requestedRelations): Relation
{
$filters = collect($request->get('filters', []))
->map(function (array $filterDescriptor) use ($request, $parentEntity) {
return $this->beforeFilterApplied($request, $parentEntity, $filterDescriptor);
})->toArray();
$request->request->add(['filters' => $filters]);
return $this->buildRelationFetchQuery($request, $parentEntity, $requestedRelations);
}
/**
* Wrapper function to build Eloquent query for fetching relation entity.
*
* @param Request $request
* @param Model $parentEntity
* @param array $requestedRelations
* @return Relation
*/
protected function buildRelationFetchQuery(
Request $request,
Model $parentEntity,
array $requestedRelations
): Relation {
return $this->buildRelationFetchQueryBase(
$request,
$parentEntity,
$requestedRelations
);
}
/**
* Builds Eloquent query for fetching relation entity.
*
* @param Request $request
* @param Model $parentEntity
* @param array $requestedRelations
* @return Relation
*/
protected function buildRelationFetchQueryBase(
Request $request,
Model $parentEntity,
array $requestedRelations
): Relation {
return $this->relationQueryBuilder->buildQuery(
$this->newRelationQuery($parentEntity), $request
);
}
/**
* Runs the given query for fetching relation entities in index method.
*
* @param Request $request
* @param Relation $query
* @param Model $parentEntity
* @param int $paginationLimit
* @return Paginator|Collection
*/
protected function runIndexFetchQuery(Request $request, Relation $query, Model $parentEntity, int $paginationLimit)
{
return $this->shouldPaginate($request, $paginationLimit) ? $query->paginate($paginationLimit) : $query->get();
}
/**
* Removes unrelated to model attributes, if any.
*
* @param Model $entity
* @return Model
*/
protected function cleanupEntity(Model $entity)
{
$entity->makeHidden('laravel_through_key');
return $entity;
}
/**
* The hooks is executed after fetching the list of relation resources.
*
* @param Request $request
* @param Model $parentEntity
* @param Paginator|Collection $entities
* @return mixed
*/
protected function afterIndex(Request $request, Model $parentEntity, $entities)
{
return null;
}
/**
* Filters, sorts, and fetches the list of resources.
*
* @param Request $request
* @param $parentKey
* @return CollectionResource
*/
public function search(Request $request, $parentKey)
{
return $this->index($request, $parentKey);
}
/**
* Create new relation resource.
*
* @param Request $request
* @param int|string $parentKey
* @return Resource
*/
public function store(Request $request, $parentKey)
{
try {
$this->startTransaction();
$result = $this->storeWithTransaction($request, $parentKey);
$this->commitTransaction();
return $result;
} catch (\Exception $exception) {
$this->rollbackTransactionAndRaise($exception);
}
}
/**
* Create new relation resource.
*
* @param Request $request
* @param int|string $parentKey
* @return Resource
*/
protected function storeWithTransaction(Request $request, $parentKey)
{
$parentQuery = $this->buildStoreParentFetchQuery($request, $parentKey);
$parentEntity = $this->runStoreParentFetchQuery($request, $parentQuery, $parentKey);
$resourceModelClass = $this->resolveResourceModelClass();
$this->authorize($this->resolveAbility('create'), [$resourceModelClass, $parentEntity]);
/** @var Model $entity */
$entity = new $resourceModelClass;
$beforeHookResult = $this->beforeStore($request, $parentEntity, $entity);
if ($this->hookResponds($beforeHookResult)) {
return $beforeHookResult;
}
$beforeSaveHookResult = $this->beforeSave($request, $parentEntity, $entity);
if ($this->hookResponds($beforeSaveHookResult)) {
return $beforeSaveHookResult;
}
$requestedRelations = $this->relationsResolver->requestedRelations($request);
if ($this->isOneToOneRelation($parentEntity)) {
$query = $this->buildStoreFetchQuery(
$request, $parentEntity, $requestedRelations
);
if ($query->exists()) {
abort(409, 'Entity already exists.');
}
}
$this->performStore(
$request,
$parentEntity,
$entity,
$this->retrieve($request),
$request->get('pivot', [])
);
$query = $this->buildStoreFetchQuery($request, $parentEntity, $requestedRelations);
$entity = $this->runStoreFetchQuery(
$request,
$query,
$parentEntity,
$entity->{$this->keyName()}
);
$entity->wasRecentlyCreated = true;
$entity = $this->cleanupEntity($entity);
if (count($this->getPivotJson())) {
$entity = $this->castPivotJsonFields($entity);
}
$afterSaveHookResult = $this->afterSave($request, $parentEntity, $entity);
if ($this->hookResponds($afterSaveHookResult)) {
return $afterSaveHookResult;
}
$afterHookResult = $this->afterStore($request, $parentEntity, $entity);
if ($this->hookResponds($afterHookResult)) {
return $afterHookResult;
}
$entity = $this->relationsResolver->guardRelations($entity, $requestedRelations);
return $this->entityResponse($entity);
}
/**
* Builds Eloquent query for fetching parent entity in store method.
*
* @param Request $request
* @param string|int $parentKey
* @return Builder
*/
protected function buildStoreParentFetchQuery(Request $request, $parentKey): Builder
{
return $this->buildParentFetchQuery($request, $parentKey);
}
/**
* Runs the given query for fetching parent entity in store method.
*
* @param Request $request
* @param Builder $query
* @param string|int $parentKey
* @return Model
*/
protected function runStoreParentFetchQuery(Request $request, Builder $query, $parentKey): Model
{
return $this->runParentFetchQuery($request, $query, $parentKey);
}
/**
* The hook is executed before creating new relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function beforeStore(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* The hook is executed before creating or updating a relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function beforeSave(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* Fills attributes on the given relation entity and stores it in database.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @param array $attributes
* @param array $pivot
*/
protected function performStore(
Request $request,
Model $parentEntity,
Model $entity,
array $attributes,
array $pivot
): void {
$this->performFill($request, $parentEntity, $entity, $attributes, $pivot);
if (!$parentEntity->{$this->getRelation()}() instanceof BelongsTo) {
$parentEntity->{$this->getRelation()}()->save($entity, $this->preparePivotFields($pivot));
} else {
$entity->save();
$parentEntity->{$this->getRelation()}()->associate($entity);
}
}
/**
* Builds Eloquent query for fetching relation entity in store method.
*
* @param Request $request
* @param Model $parentEntity
* @param array $requestedRelations
* @return Relation
*/
protected function buildStoreFetchQuery(Request $request, Model $parentEntity, array $requestedRelations): Relation
{
return $this->buildRelationFetchQuery($request, $parentEntity, $requestedRelations);
}
/**
* Runs the given query for fetching relation entity in store method.
*
* @param Request $request
* @param Relation $query
* @param Model $parentEntity
* @param string|int $relatedKey
* @return Model
*/
protected function runStoreFetchQuery(Request $request, Relation $query, Model $parentEntity, $relatedKey): Model
{
return $this->runRelationFetchQuery($request, $query, $parentEntity, $relatedKey);
}
/**
* The hook is executed after creating or updating a relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function afterSave(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* The hook is executed after creating new relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function afterStore(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* Fetch a relation resource.
*
* @param Request $request
* @param int|string $parentKey
* @param int|string|null $relatedKey
* @return Resource
*/
public function show(Request $request, $parentKey, $relatedKey = null)
{
$parentQuery = $this->buildShowParentFetchQuery($request, $parentKey);
$parentEntity = $this->runShowParentFetchQuery($request, $parentQuery, $parentKey);
$beforeHookResult = $this->beforeShow($request, $parentEntity, $relatedKey);
if ($this->hookResponds($beforeHookResult)) {
return $beforeHookResult;
}
$requestedRelations = $this->relationsResolver->requestedRelations($request);
$query = $this->buildShowFetchQuery($request, $parentEntity, $requestedRelations);
$entity = $this->runShowFetchQuery($request, $query, $parentEntity, $relatedKey);
$this->authorize($this->resolveAbility('show'), [$entity, $parentEntity]);
$entity = $this->cleanupEntity($entity);
if (count($this->getPivotJson())) {
$entity = $this->castPivotJsonFields($entity);
}
$afterHookResult = $this->afterShow($request, $parentEntity, $entity);
if ($this->hookResponds($afterHookResult)) {
return $afterHookResult;
}
$entity = $this->relationsResolver->guardRelations($entity, $requestedRelations);
return $this->entityResponse($entity);
}
/**
* The hook is executed before fetching relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param int|string|null $key
* @return mixed
*/
protected function beforeShow(Request $request, Model $parentEntity, $key)
{
return null;
}
/**
* Builds Eloquent query for fetching parent entity in show method.
*
* @param Request $request
* @param string|int $parentKey
* @return Builder
*/
protected function buildShowParentFetchQuery(Request $request, $parentKey): Builder
{
return $this->buildParentFetchQuery($request, $parentKey);
}
/**
* Runs the given query for fetching parent entity in show method.
*
* @param Request $request
* @param Builder $query
* @param string|int $parentKey
* @return Model
*/
protected function runShowParentFetchQuery(Request $request, Builder $query, $parentKey): Model
{
return $this->runParentFetchQuery($request, $query, $parentKey);
}
/**
* Builds Eloquent query for fetching relation entity in show method.
*
* @param Request $request
* @param Model $parentEntity
* @param array $requestedRelations
* @return Relation
*/
protected function buildShowFetchQuery(Request $request, Model $parentEntity, array $requestedRelations): Relation
{
return $this->buildRelationFetchQuery($request, $parentEntity, $requestedRelations);
}
/**
* Runs the given query for fetching relation entity in show method.
*
* @param Request $request
* @param Relation $query
* @param Model $parentEntity
* @param string|int $relatedKey
* @return Model
*/
protected function runShowFetchQuery(Request $request, Relation $query, Model $parentEntity, $relatedKey): Model
{
return $this->runRelationFetchQuery($request, $query, $parentEntity, $relatedKey);
}
/**
* Wrapper function to run the given query for fetching relation entity.
*
* @param Request $request
* @param Relation $query
* @param Model $parentEntity
* @param string|int $relatedKey
* @return Model
*/
protected function runRelationFetchQuery(Request $request, Relation $query, Model $parentEntity, $relatedKey): Model
{
return $this->runRelationFetchQueryBase(
$request,
$query,
$parentEntity,
$relatedKey
);
}
/**
* Runs the given query for fetching relation entity.
*
* @param Request $request
* @param Relation $query
* @param Model $parentEntity
* @param string|int $relatedKey
* @return Model
*/
protected function runRelationFetchQueryBase(
Request $request,
Relation $query,
Model $parentEntity,
$relatedKey
): Model {
if ($this->isOneToOneRelation($parentEntity)) {
return $query->firstOrFail();
}
$this->abortIfMissingRelatedID($relatedKey);
return $query->where($this->resolveQualifiedKeyName(), $relatedKey)->firstOrFail();
}
/**
* Determines whether controller relation is one-to-one or not.
*
* @param Model $parentEntity
* @return bool
*/
protected function isOneToOneRelation(Model $parentEntity)
{
$relation = $parentEntity->{$this->getRelation()}();
return $relation instanceof HasOne || $relation instanceof MorphOne || $relation instanceof BelongsTo || $relation instanceof HasOneThrough;
}
/**
* Throws exception, if related ID is undefined and relation type is not one-to-one.
*
* @param int|string|null $relatedKey
*/
protected function abortIfMissingRelatedID($relatedKey)
{
if ($relatedKey) {
return;
}
throw new InvalidArgumentException('Relation key is required, if relation type is not one-to-one');
}
/**
* The hook is executed after fetching a relation resource
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function afterShow(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* Update a relation resource in a transaction-safe way.
*
* @param Request $request
* @param int|string $parentKey
* @param int|string|null $relatedKey
* @return Resource
*/
public function update(Request $request, $parentKey, $relatedKey = null)
{
try {
$this->startTransaction();
$result = $this->updateWithTransaction($request, $parentKey, $relatedKey);
$this->commitTransaction();
return $result;
} catch (\Exception $exception) {
$this->rollbackTransactionAndRaise($exception);
}
}
/**
* Update a relation resource.
*
* @param Request $request
* @param int|string $parentKey
* @param int|string|null $relatedKey
* @return Resource
*/
protected function updateWithTransaction(Request $request, $parentKey, $relatedKey = null)
{
$parentQuery = $this->buildUpdateParentFetchQuery($request, $parentKey);
$parentEntity = $this->runUpdateParentFetchQuery($request, $parentQuery, $parentKey);
$requestedRelations = $this->relationsResolver->requestedRelations($request);
$query = $this->buildUpdateFetchQuery($request, $parentEntity, $requestedRelations);
$entity = $this->runUpdateFetchQuery($request, $query, $parentEntity, $relatedKey);
$this->authorize($this->resolveAbility('update'), [$entity, $parentEntity]);
$beforeHookResult = $this->beforeUpdate($request, $parentEntity, $entity);
if ($this->hookResponds($beforeHookResult)) {
return $beforeHookResult;
}
$beforeSaveHookResult = $this->beforeSave($request, $parentEntity, $entity);
if ($this->hookResponds($beforeSaveHookResult)) {
return $beforeSaveHookResult;
}
$this->performUpdate(
$request,
$parentEntity,
$entity,
$this->retrieve($request),
$request->get('pivot', [])
);
$entity = $this->refreshUpdatedEntity(
$request, $parentEntity, $requestedRelations, $relatedKey
);
$entity = $this->cleanupEntity($entity);
if (count($this->getPivotJson())) {
$entity = $this->castPivotJsonFields($entity);
}
$afterSaveHookResult = $this->afterSave($request, $parentEntity, $entity);
if ($this->hookResponds($afterSaveHookResult)) {
return $afterSaveHookResult;
}
$afterHookResult = $this->afterUpdate($request, $parentEntity, $entity);
if ($this->hookResponds($afterHookResult)) {
return $afterHookResult;
}
$entity = $this->relationsResolver->guardRelations($entity, $requestedRelations);
return $this->entityResponse($entity);
}
/**
* Builds Eloquent query for fetching parent entity in update method.
*
* @param Request $request
* @param string|int $parentKey
* @return Builder
*/
protected function buildUpdateParentFetchQuery(Request $request, $parentKey): Builder
{
return $this->buildParentFetchQuery($request, $parentKey);
}
/**
* Runs the given query for fetching parent entity in update method.
*
* @param Request $request
* @param Builder $query
* @param string|int $parentKey
* @return Model
*/
protected function runUpdateParentFetchQuery(Request $request, Builder $query, $parentKey): Model
{
return $this->runParentFetchQuery($request, $query, $parentKey);
}
/**
* Builds Eloquent query for fetching relation entity in update method.
*
* @param Request $request
* @param Model $parentEntity
* @param array $requestedRelations
* @return Relation
*/
protected function buildUpdateFetchQuery(Request $request, Model $parentEntity, array $requestedRelations): Relation
{
return $this->buildRelationFetchQuery($request, $parentEntity, $requestedRelations);
}
/**
* Runs the given query for fetching relation entity in update method.
*
* @param Request $request
* @param Relation $query
* @param Model $parentEntity
* @param string|int $relatedKey
* @return Model
*/
protected function runUpdateFetchQuery(Request $request, Relation $query, Model $parentEntity, $relatedKey): Model
{
return $this->runRelationFetchQuery($request, $query, $parentEntity, $relatedKey);
}
/**
* Fetches the relation model that has just been updated using the given key.
*
* @param Request $request
* @param Model $parentEntity
* @param array $requestedRelations
* @param string|int $relatedKey
* @return Model
*/
protected function refreshUpdatedEntity(
Request $request,
Model $parentEntity,
array $requestedRelations,
$relatedKey
): Model {
$query = $this->buildRelationFetchQueryBase(
$request,
$parentEntity,
$requestedRelations
);
return $this->runRelationFetchQueryBase(
$request,
$query,
$parentEntity,
$relatedKey
);
}
/**
* The hook is executed before updating a relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function beforeUpdate(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* Fills attributes on the given relation entity and persists changes in database.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @param array $attributes
* @param array $pivot
*/
protected function performUpdate(
Request $request,
Model $parentEntity,
Model $entity,
array $attributes,
array $pivot
): void {
$this->performFill($request, $parentEntity, $entity, $attributes, $pivot);
$entity->save();
$relation = $parentEntity->{$this->getRelation()}();
if ($relation instanceof BelongsToMany || $relation instanceof MorphToMany) {
if (count($pivotFields = $this->preparePivotFields($pivot))) {
$relation->updateExistingPivot($entity->getKey(), $pivotFields);
}
}
}
/**
* The hook is executed after updating a relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function afterUpdate(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* Delete a relation resource.
*
* @param Request $request
* @param int|string $parentKey
* @param int|string|null $relatedKey
* @return Resource
* @throws Exception
*/
public function destroy(Request $request, $parentKey, $relatedKey = null)
{
try {
$this->startTransaction();
$result = $this->destroyWithTransaction($request, $parentKey, $relatedKey);
$this->commitTransaction();
return $result;
} catch (\Exception $exception) {
$this->rollbackTransactionAndRaise($exception);
}
}
/**
* Delete a relation resource.
*
* @param Request $request
* @param int|string $parentKey
* @param int|string|null $relatedKey
* @return Resource
* @throws Exception
*/
protected function destroyWithTransaction(Request $request, $parentKey, $relatedKey = null)
{
$parentQuery = $this->buildDestroyParentFetchQuery($request, $parentKey);
$parentEntity = $this->runDestroyParentFetchQuery($request, $parentQuery, $parentKey);
$softDeletes = $this->softDeletes($this->resolveResourceModelClass());
$forceDeletes = $this->forceDeletes($request, $softDeletes);
$requestedRelations = $this->relationsResolver->requestedRelations($request);
$query = $this->buildDestroyFetchQuery($request, $parentEntity, $requestedRelations, $softDeletes);
$entity = $this->runDestroyFetchQuery($request, $query, $parentEntity, $relatedKey);
if ($this->isResourceTrashed($entity, $softDeletes, $forceDeletes)) {
abort(404);
}
$this->authorize($this->resolveAbility($forceDeletes ? 'forceDelete' : 'delete'), [$entity, $parentEntity]);
$beforeHookResult = $this->beforeDestroy($request, $parentEntity, $entity);
if ($this->hookResponds($beforeHookResult)) {
return $beforeHookResult;
}
if (!$forceDeletes) {
$this->performDestroy($entity);
if ($softDeletes) {
$entity = $this->runDestroyFetchQuery($request, $query, $parentEntity, $relatedKey);
}
} else {
$this->performForceDestroy($entity);
}
$entity = $this->cleanupEntity($entity);
if (count($this->getPivotJson())) {
$entity = $this->castPivotJsonFields($entity);
}
$afterHookResult = $this->afterDestroy($request, $parentEntity, $entity);
if ($this->hookResponds($afterHookResult)) {
return $afterHookResult;
}
$entity = $this->relationsResolver->guardRelations($entity, $requestedRelations);
return $this->entityResponse($entity);
}
/**
* Builds Eloquent query for fetching parent entity in destroy method.
*
* @param Request $request
* @param string|int $parentKey
* @return Builder
*/
protected function buildDestroyParentFetchQuery(Request $request, $parentKey): Builder
{
return $this->buildParentFetchQuery($request, $parentKey);
}
/**
* Runs the given query for fetching parent entity in destroy method.
*
* @param Request $request
* @param Builder $query
* @param string|int $parentKey
* @return Model
*/
protected function runDestroyParentFetchQuery(Request $request, Builder $query, $parentKey): Model
{
return $this->runParentFetchQuery($request, $query, $parentKey);
}
/**
* Builds Eloquent query for fetching relation entity in destroy method.
*
* @param Request $request
* @param Model $parentEntity
* @param array $requestedRelations
* @param bool $softDeletes
* @return Relation
*/
protected function buildDestroyFetchQuery(
Request $request,
Model $parentEntity,
array $requestedRelations,
bool $softDeletes
): Relation {
return $this->buildRelationFetchQuery($request, $parentEntity, $requestedRelations)
->when(
$softDeletes,
function ($query) {
$query->withTrashed();
}
);
}
/**
* Runs the given query for fetching relation entity in destroy method.
*
* @param Request $request
* @param Relation $query
* @param Model $parentEntity
* @param string|int $relatedKey
* @return Model
*/
protected function runDestroyFetchQuery(Request $request, Relation $query, Model $parentEntity, $relatedKey): Model
{
return $this->runRelationFetchQuery($request, $query, $parentEntity, $relatedKey);
}
/**
* The hook is executed before deleting a relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function beforeDestroy(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* Deletes or trashes the given relation entity from database.
*
* @param Model $entity
* @throws Exception
*/
protected function performDestroy(Model $entity): void
{
$entity->delete();
}
/**
* Deletes the given relation entity from database, even if it is soft deletable.
*
* @param Model $entity
*/
protected function performForceDestroy(Model $entity): void
{
$entity->forceDelete();
}
/**
* The hook is executed after deleting a relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function afterDestroy(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* Restores a previously deleted relation resource in a transaction-save way.
*
* @param Request $request
* @param int|string $parentKey
* @param int|string|null $relatedKey
* @return Resource
*/
public function restore(Request $request, $parentKey, $relatedKey = null)
{
try {
$this->startTransaction();
$result = $this->restoreWithTransaction($request, $parentKey, $relatedKey);
$this->commitTransaction();
return $result;
} catch (\Exception $exception) {
$this->rollbackTransactionAndRaise($exception);
}
}
/**
* Restores a previously deleted relation resource.
*
* @param Request $request
* @param int|string $parentKey
* @param int|string|null $relatedKey
* @return Resource
*/
protected function restoreWithTransaction(Request $request, $parentKey, $relatedKey = null)
{
$parentQuery = $this->buildRestoreParentFetchQuery($request, $parentKey);
$parentEntity = $this->runRestoreParentFetchQuery($request, $parentQuery, $parentKey);
$requestedRelations = $this->relationsResolver->requestedRelations($request);
$query = $this->buildRestoreFetchQuery($request, $parentEntity, $requestedRelations);
$entity = $this->runRestoreFetchQuery($request, $query, $parentEntity, $relatedKey);
$this->authorize($this->resolveAbility('restore'), [$entity, $parentEntity]);
$beforeHookResult = $this->beforeRestore($request, $parentEntity, $entity);
if ($this->hookResponds($beforeHookResult)) {
return $beforeHookResult;
}
$this->performRestore($entity);
$entity = $this->runRestoreFetchQuery($request, $query, $parentEntity, $relatedKey);
$entity = $this->cleanupEntity($entity);
if (count($this->getPivotJson())) {
$entity = $this->castPivotJsonFields($entity);
}
$afterHookResult = $this->afterRestore($request, $parentEntity, $entity);
if ($this->hookResponds($afterHookResult)) {
return $afterHookResult;
}
$entity = $this->relationsResolver->guardRelations($entity, $requestedRelations);
return $this->entityResponse($entity);
}
/**
* Builds Eloquent query for fetching parent entity in restore method.
*
* @param Request $request
* @param string|int $parentKey
* @return Builder
*/
protected function buildRestoreParentFetchQuery(Request $request, $parentKey): Builder
{
return $this->buildParentFetchQuery($request, $parentKey);
}
/**
* Runs the given query for fetching parent entity in restore method.
*
* @param Request $request
* @param Builder $query
* @param string|int $parentKey
* @return Model
*/
protected function runRestoreParentFetchQuery(Request $request, Builder $query, $parentKey): Model
{
return $this->runParentFetchQuery($request, $query, $parentKey);
}
/**
* Builds Eloquent query for fetching relation entity in restore method.
*
* @param Request $request
* @param Model $parentEntity
* @param array $requestedRelations
* @return Relation
*/
protected function buildRestoreFetchQuery(
Request $request,
Model $parentEntity,
array $requestedRelations
): Relation {
return $this->buildRelationFetchQuery($request, $parentEntity, $requestedRelations)
->withTrashed();
}
/**
* Runs the given query for fetching relation entity in restore method.
*
* @param Request $request
* @param Relation $query
* @param Model $parentEntity
* @param string|int $relatedKey
* @return Model
*/
protected function runRestoreFetchQuery(Request $request, Relation $query, Model $parentEntity, $relatedKey): Model
{
return $this->runRelationFetchQuery($request, $query, $parentEntity, $relatedKey);
}
/**
* The hook is executed before restoring a relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function beforeRestore(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* Restores the given relation entity.
*
* @param Model|SoftDeletes $entity
*/
protected function performRestore(Model $entity): void
{
$entity->restore();
}
/**
* The hook is executed after restoring a relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function afterRestore(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* Fills attributes on the given relation entity.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @param array $attributes
* @param array $pivot
*/
protected function performFill(
Request $request,
Model $parentEntity,
Model $entity,
array $attributes,
array $pivot
): void {
$entity->fill(
Arr::except($attributes, array_keys($entity->getDirty()))
);
}
/**
* @param Request $request
* @param Model $parentEntity
* @param array $filterDescriptor
* @return array
*/
protected function beforeFilterApplied(Request $request, Model $parentEntity, array $filterDescriptor): array
{
return $filterDescriptor;
}
}
?>
Did this file decode correctly?
Original Code
<?php
namespace Orion\Concerns;
use Exception;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Arr;
use InvalidArgumentException;
use Orion\Http\Requests\Request;
use Orion\Http\Resources\CollectionResource;
use Orion\Http\Resources\Resource;
trait HandlesRelationStandardOperations
{
/**
* Fetch the list of relation resources.
*
* @param Request $request
* @param int|string $parentKey
* @return CollectionResource
*/
public function index(Request $request, $parentKey)
{
$requestedRelations = $this->relationsResolver->requestedRelations($request);
$parentQuery = $this->buildIndexParentFetchQuery($request, $parentKey);
$parentEntity = $this->runIndexParentFetchQuery($request, $parentQuery, $parentKey);
$this->authorize($this->resolveAbility('index'), [$this->resolveResourceModelClass(), $parentEntity]);
$beforeHookResult = $this->beforeIndex($request, $parentEntity);
if ($this->hookResponds($beforeHookResult)) {
return $beforeHookResult;
}
$this->authorize($this->resolveAbility('show'), $parentEntity);
$query = $this->buildIndexFetchQuery($request, $parentEntity, $requestedRelations);
$entities = $this->runIndexFetchQuery(
$request,
$query,
$parentEntity,
$this->paginator->resolvePaginationLimit($request)
);
($entities instanceof Paginator ? $entities->getCollection() : $entities)->transform(
function ($entity) {
$entity = $this->cleanupEntity($entity);
if (count($this->getPivotJson())) {
$entity = $this->castPivotJsonFields($entity);
}
return $entity;
}
);
$afterHookResult = $this->afterIndex($request, $parentEntity, $entities);
if ($this->hookResponds($afterHookResult)) {
return $afterHookResult;
}
$this->relationsResolver->guardRelationsForCollection(
$entities instanceof Paginator ? $entities->getCollection() : $entities,
$requestedRelations
);
return $this->collectionResponse($entities);
}
/**
* The hooks is executed before fetching the list of relation resources.
*
* @param Request $request
* @param Model $parentEntity
* @return mixed
*/
protected function beforeIndex(Request $request, Model $parentEntity)
{
return null;
}
/**
* Builds Eloquent query for fetching parent entity in index method.
*
* @param Request $request
* @param string|int $parentKey
* @return Builder
*/
protected function buildIndexParentFetchQuery(Request $request, $parentKey): Builder
{
return $this->buildParentFetchQuery($request, $parentKey);
}
/**
* Builds Eloquent query for fetching parent entity.
*
* @param Request $request
* @param string|int $parentKey
* @return Builder
*/
protected function buildParentFetchQuery(Request $request, $parentKey): Builder
{
return $this->queryBuilder->buildQuery($this->newModelQuery(), $request);
}
/**
* Runs the given query for fetching parent entity in index method.
*
* @param Request $request
* @param Builder $query
* @param string|int $parentKey
* @return Model
*/
protected function runIndexParentFetchQuery(Request $request, Builder $query, $parentKey): Model
{
return $this->runParentFetchQuery($request, $query, $parentKey);
}
/**
* Runs the given query for fetching parent entity.
*
* @param Request $request
* @param Builder $query
* @param string|int $parentKey
* @return Model
*/
protected function runParentFetchQuery(Request $request, Builder $query, $parentKey): Model
{
return $query->where($this->resolveQualifiedParentKeyName(), $parentKey)->firstOrFail();
}
/**
* Builds Eloquent query for fetching relation entities in index method.
*
* @param Request $request
* @param Model $parentEntity
* @param array $requestedRelations
* @return Relation
*/
protected function buildIndexFetchQuery(Request $request, Model $parentEntity, array $requestedRelations): Relation
{
$filters = collect($request->get('filters', []))
->map(function (array $filterDescriptor) use ($request, $parentEntity) {
return $this->beforeFilterApplied($request, $parentEntity, $filterDescriptor);
})->toArray();
$request->request->add(['filters' => $filters]);
return $this->buildRelationFetchQuery($request, $parentEntity, $requestedRelations);
}
/**
* Wrapper function to build Eloquent query for fetching relation entity.
*
* @param Request $request
* @param Model $parentEntity
* @param array $requestedRelations
* @return Relation
*/
protected function buildRelationFetchQuery(
Request $request,
Model $parentEntity,
array $requestedRelations
): Relation {
return $this->buildRelationFetchQueryBase(
$request,
$parentEntity,
$requestedRelations
);
}
/**
* Builds Eloquent query for fetching relation entity.
*
* @param Request $request
* @param Model $parentEntity
* @param array $requestedRelations
* @return Relation
*/
protected function buildRelationFetchQueryBase(
Request $request,
Model $parentEntity,
array $requestedRelations
): Relation {
return $this->relationQueryBuilder->buildQuery(
$this->newRelationQuery($parentEntity), $request
);
}
/**
* Runs the given query for fetching relation entities in index method.
*
* @param Request $request
* @param Relation $query
* @param Model $parentEntity
* @param int $paginationLimit
* @return Paginator|Collection
*/
protected function runIndexFetchQuery(Request $request, Relation $query, Model $parentEntity, int $paginationLimit)
{
return $this->shouldPaginate($request, $paginationLimit) ? $query->paginate($paginationLimit) : $query->get();
}
/**
* Removes unrelated to model attributes, if any.
*
* @param Model $entity
* @return Model
*/
protected function cleanupEntity(Model $entity)
{
$entity->makeHidden('laravel_through_key');
return $entity;
}
/**
* The hooks is executed after fetching the list of relation resources.
*
* @param Request $request
* @param Model $parentEntity
* @param Paginator|Collection $entities
* @return mixed
*/
protected function afterIndex(Request $request, Model $parentEntity, $entities)
{
return null;
}
/**
* Filters, sorts, and fetches the list of resources.
*
* @param Request $request
* @param $parentKey
* @return CollectionResource
*/
public function search(Request $request, $parentKey)
{
return $this->index($request, $parentKey);
}
/**
* Create new relation resource.
*
* @param Request $request
* @param int|string $parentKey
* @return Resource
*/
public function store(Request $request, $parentKey)
{
try {
$this->startTransaction();
$result = $this->storeWithTransaction($request, $parentKey);
$this->commitTransaction();
return $result;
} catch (\Exception $exception) {
$this->rollbackTransactionAndRaise($exception);
}
}
/**
* Create new relation resource.
*
* @param Request $request
* @param int|string $parentKey
* @return Resource
*/
protected function storeWithTransaction(Request $request, $parentKey)
{
$parentQuery = $this->buildStoreParentFetchQuery($request, $parentKey);
$parentEntity = $this->runStoreParentFetchQuery($request, $parentQuery, $parentKey);
$resourceModelClass = $this->resolveResourceModelClass();
$this->authorize($this->resolveAbility('create'), [$resourceModelClass, $parentEntity]);
/** @var Model $entity */
$entity = new $resourceModelClass;
$beforeHookResult = $this->beforeStore($request, $parentEntity, $entity);
if ($this->hookResponds($beforeHookResult)) {
return $beforeHookResult;
}
$beforeSaveHookResult = $this->beforeSave($request, $parentEntity, $entity);
if ($this->hookResponds($beforeSaveHookResult)) {
return $beforeSaveHookResult;
}
$requestedRelations = $this->relationsResolver->requestedRelations($request);
if ($this->isOneToOneRelation($parentEntity)) {
$query = $this->buildStoreFetchQuery(
$request, $parentEntity, $requestedRelations
);
if ($query->exists()) {
abort(409, 'Entity already exists.');
}
}
$this->performStore(
$request,
$parentEntity,
$entity,
$this->retrieve($request),
$request->get('pivot', [])
);
$query = $this->buildStoreFetchQuery($request, $parentEntity, $requestedRelations);
$entity = $this->runStoreFetchQuery(
$request,
$query,
$parentEntity,
$entity->{$this->keyName()}
);
$entity->wasRecentlyCreated = true;
$entity = $this->cleanupEntity($entity);
if (count($this->getPivotJson())) {
$entity = $this->castPivotJsonFields($entity);
}
$afterSaveHookResult = $this->afterSave($request, $parentEntity, $entity);
if ($this->hookResponds($afterSaveHookResult)) {
return $afterSaveHookResult;
}
$afterHookResult = $this->afterStore($request, $parentEntity, $entity);
if ($this->hookResponds($afterHookResult)) {
return $afterHookResult;
}
$entity = $this->relationsResolver->guardRelations($entity, $requestedRelations);
return $this->entityResponse($entity);
}
/**
* Builds Eloquent query for fetching parent entity in store method.
*
* @param Request $request
* @param string|int $parentKey
* @return Builder
*/
protected function buildStoreParentFetchQuery(Request $request, $parentKey): Builder
{
return $this->buildParentFetchQuery($request, $parentKey);
}
/**
* Runs the given query for fetching parent entity in store method.
*
* @param Request $request
* @param Builder $query
* @param string|int $parentKey
* @return Model
*/
protected function runStoreParentFetchQuery(Request $request, Builder $query, $parentKey): Model
{
return $this->runParentFetchQuery($request, $query, $parentKey);
}
/**
* The hook is executed before creating new relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function beforeStore(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* The hook is executed before creating or updating a relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function beforeSave(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* Fills attributes on the given relation entity and stores it in database.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @param array $attributes
* @param array $pivot
*/
protected function performStore(
Request $request,
Model $parentEntity,
Model $entity,
array $attributes,
array $pivot
): void {
$this->performFill($request, $parentEntity, $entity, $attributes, $pivot);
if (!$parentEntity->{$this->getRelation()}() instanceof BelongsTo) {
$parentEntity->{$this->getRelation()}()->save($entity, $this->preparePivotFields($pivot));
} else {
$entity->save();
$parentEntity->{$this->getRelation()}()->associate($entity);
}
}
/**
* Builds Eloquent query for fetching relation entity in store method.
*
* @param Request $request
* @param Model $parentEntity
* @param array $requestedRelations
* @return Relation
*/
protected function buildStoreFetchQuery(Request $request, Model $parentEntity, array $requestedRelations): Relation
{
return $this->buildRelationFetchQuery($request, $parentEntity, $requestedRelations);
}
/**
* Runs the given query for fetching relation entity in store method.
*
* @param Request $request
* @param Relation $query
* @param Model $parentEntity
* @param string|int $relatedKey
* @return Model
*/
protected function runStoreFetchQuery(Request $request, Relation $query, Model $parentEntity, $relatedKey): Model
{
return $this->runRelationFetchQuery($request, $query, $parentEntity, $relatedKey);
}
/**
* The hook is executed after creating or updating a relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function afterSave(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* The hook is executed after creating new relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function afterStore(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* Fetch a relation resource.
*
* @param Request $request
* @param int|string $parentKey
* @param int|string|null $relatedKey
* @return Resource
*/
public function show(Request $request, $parentKey, $relatedKey = null)
{
$parentQuery = $this->buildShowParentFetchQuery($request, $parentKey);
$parentEntity = $this->runShowParentFetchQuery($request, $parentQuery, $parentKey);
$beforeHookResult = $this->beforeShow($request, $parentEntity, $relatedKey);
if ($this->hookResponds($beforeHookResult)) {
return $beforeHookResult;
}
$requestedRelations = $this->relationsResolver->requestedRelations($request);
$query = $this->buildShowFetchQuery($request, $parentEntity, $requestedRelations);
$entity = $this->runShowFetchQuery($request, $query, $parentEntity, $relatedKey);
$this->authorize($this->resolveAbility('show'), [$entity, $parentEntity]);
$entity = $this->cleanupEntity($entity);
if (count($this->getPivotJson())) {
$entity = $this->castPivotJsonFields($entity);
}
$afterHookResult = $this->afterShow($request, $parentEntity, $entity);
if ($this->hookResponds($afterHookResult)) {
return $afterHookResult;
}
$entity = $this->relationsResolver->guardRelations($entity, $requestedRelations);
return $this->entityResponse($entity);
}
/**
* The hook is executed before fetching relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param int|string|null $key
* @return mixed
*/
protected function beforeShow(Request $request, Model $parentEntity, $key)
{
return null;
}
/**
* Builds Eloquent query for fetching parent entity in show method.
*
* @param Request $request
* @param string|int $parentKey
* @return Builder
*/
protected function buildShowParentFetchQuery(Request $request, $parentKey): Builder
{
return $this->buildParentFetchQuery($request, $parentKey);
}
/**
* Runs the given query for fetching parent entity in show method.
*
* @param Request $request
* @param Builder $query
* @param string|int $parentKey
* @return Model
*/
protected function runShowParentFetchQuery(Request $request, Builder $query, $parentKey): Model
{
return $this->runParentFetchQuery($request, $query, $parentKey);
}
/**
* Builds Eloquent query for fetching relation entity in show method.
*
* @param Request $request
* @param Model $parentEntity
* @param array $requestedRelations
* @return Relation
*/
protected function buildShowFetchQuery(Request $request, Model $parentEntity, array $requestedRelations): Relation
{
return $this->buildRelationFetchQuery($request, $parentEntity, $requestedRelations);
}
/**
* Runs the given query for fetching relation entity in show method.
*
* @param Request $request
* @param Relation $query
* @param Model $parentEntity
* @param string|int $relatedKey
* @return Model
*/
protected function runShowFetchQuery(Request $request, Relation $query, Model $parentEntity, $relatedKey): Model
{
return $this->runRelationFetchQuery($request, $query, $parentEntity, $relatedKey);
}
/**
* Wrapper function to run the given query for fetching relation entity.
*
* @param Request $request
* @param Relation $query
* @param Model $parentEntity
* @param string|int $relatedKey
* @return Model
*/
protected function runRelationFetchQuery(Request $request, Relation $query, Model $parentEntity, $relatedKey): Model
{
return $this->runRelationFetchQueryBase(
$request,
$query,
$parentEntity,
$relatedKey
);
}
/**
* Runs the given query for fetching relation entity.
*
* @param Request $request
* @param Relation $query
* @param Model $parentEntity
* @param string|int $relatedKey
* @return Model
*/
protected function runRelationFetchQueryBase(
Request $request,
Relation $query,
Model $parentEntity,
$relatedKey
): Model {
if ($this->isOneToOneRelation($parentEntity)) {
return $query->firstOrFail();
}
$this->abortIfMissingRelatedID($relatedKey);
return $query->where($this->resolveQualifiedKeyName(), $relatedKey)->firstOrFail();
}
/**
* Determines whether controller relation is one-to-one or not.
*
* @param Model $parentEntity
* @return bool
*/
protected function isOneToOneRelation(Model $parentEntity)
{
$relation = $parentEntity->{$this->getRelation()}();
return $relation instanceof HasOne || $relation instanceof MorphOne || $relation instanceof BelongsTo || $relation instanceof HasOneThrough;
}
/**
* Throws exception, if related ID is undefined and relation type is not one-to-one.
*
* @param int|string|null $relatedKey
*/
protected function abortIfMissingRelatedID($relatedKey)
{
if ($relatedKey) {
return;
}
throw new InvalidArgumentException('Relation key is required, if relation type is not one-to-one');
}
/**
* The hook is executed after fetching a relation resource
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function afterShow(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* Update a relation resource in a transaction-safe way.
*
* @param Request $request
* @param int|string $parentKey
* @param int|string|null $relatedKey
* @return Resource
*/
public function update(Request $request, $parentKey, $relatedKey = null)
{
try {
$this->startTransaction();
$result = $this->updateWithTransaction($request, $parentKey, $relatedKey);
$this->commitTransaction();
return $result;
} catch (\Exception $exception) {
$this->rollbackTransactionAndRaise($exception);
}
}
/**
* Update a relation resource.
*
* @param Request $request
* @param int|string $parentKey
* @param int|string|null $relatedKey
* @return Resource
*/
protected function updateWithTransaction(Request $request, $parentKey, $relatedKey = null)
{
$parentQuery = $this->buildUpdateParentFetchQuery($request, $parentKey);
$parentEntity = $this->runUpdateParentFetchQuery($request, $parentQuery, $parentKey);
$requestedRelations = $this->relationsResolver->requestedRelations($request);
$query = $this->buildUpdateFetchQuery($request, $parentEntity, $requestedRelations);
$entity = $this->runUpdateFetchQuery($request, $query, $parentEntity, $relatedKey);
$this->authorize($this->resolveAbility('update'), [$entity, $parentEntity]);
$beforeHookResult = $this->beforeUpdate($request, $parentEntity, $entity);
if ($this->hookResponds($beforeHookResult)) {
return $beforeHookResult;
}
$beforeSaveHookResult = $this->beforeSave($request, $parentEntity, $entity);
if ($this->hookResponds($beforeSaveHookResult)) {
return $beforeSaveHookResult;
}
$this->performUpdate(
$request,
$parentEntity,
$entity,
$this->retrieve($request),
$request->get('pivot', [])
);
$entity = $this->refreshUpdatedEntity(
$request, $parentEntity, $requestedRelations, $relatedKey
);
$entity = $this->cleanupEntity($entity);
if (count($this->getPivotJson())) {
$entity = $this->castPivotJsonFields($entity);
}
$afterSaveHookResult = $this->afterSave($request, $parentEntity, $entity);
if ($this->hookResponds($afterSaveHookResult)) {
return $afterSaveHookResult;
}
$afterHookResult = $this->afterUpdate($request, $parentEntity, $entity);
if ($this->hookResponds($afterHookResult)) {
return $afterHookResult;
}
$entity = $this->relationsResolver->guardRelations($entity, $requestedRelations);
return $this->entityResponse($entity);
}
/**
* Builds Eloquent query for fetching parent entity in update method.
*
* @param Request $request
* @param string|int $parentKey
* @return Builder
*/
protected function buildUpdateParentFetchQuery(Request $request, $parentKey): Builder
{
return $this->buildParentFetchQuery($request, $parentKey);
}
/**
* Runs the given query for fetching parent entity in update method.
*
* @param Request $request
* @param Builder $query
* @param string|int $parentKey
* @return Model
*/
protected function runUpdateParentFetchQuery(Request $request, Builder $query, $parentKey): Model
{
return $this->runParentFetchQuery($request, $query, $parentKey);
}
/**
* Builds Eloquent query for fetching relation entity in update method.
*
* @param Request $request
* @param Model $parentEntity
* @param array $requestedRelations
* @return Relation
*/
protected function buildUpdateFetchQuery(Request $request, Model $parentEntity, array $requestedRelations): Relation
{
return $this->buildRelationFetchQuery($request, $parentEntity, $requestedRelations);
}
/**
* Runs the given query for fetching relation entity in update method.
*
* @param Request $request
* @param Relation $query
* @param Model $parentEntity
* @param string|int $relatedKey
* @return Model
*/
protected function runUpdateFetchQuery(Request $request, Relation $query, Model $parentEntity, $relatedKey): Model
{
return $this->runRelationFetchQuery($request, $query, $parentEntity, $relatedKey);
}
/**
* Fetches the relation model that has just been updated using the given key.
*
* @param Request $request
* @param Model $parentEntity
* @param array $requestedRelations
* @param string|int $relatedKey
* @return Model
*/
protected function refreshUpdatedEntity(
Request $request,
Model $parentEntity,
array $requestedRelations,
$relatedKey
): Model {
$query = $this->buildRelationFetchQueryBase(
$request,
$parentEntity,
$requestedRelations
);
return $this->runRelationFetchQueryBase(
$request,
$query,
$parentEntity,
$relatedKey
);
}
/**
* The hook is executed before updating a relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function beforeUpdate(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* Fills attributes on the given relation entity and persists changes in database.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @param array $attributes
* @param array $pivot
*/
protected function performUpdate(
Request $request,
Model $parentEntity,
Model $entity,
array $attributes,
array $pivot
): void {
$this->performFill($request, $parentEntity, $entity, $attributes, $pivot);
$entity->save();
$relation = $parentEntity->{$this->getRelation()}();
if ($relation instanceof BelongsToMany || $relation instanceof MorphToMany) {
if (count($pivotFields = $this->preparePivotFields($pivot))) {
$relation->updateExistingPivot($entity->getKey(), $pivotFields);
}
}
}
/**
* The hook is executed after updating a relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function afterUpdate(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* Delete a relation resource.
*
* @param Request $request
* @param int|string $parentKey
* @param int|string|null $relatedKey
* @return Resource
* @throws Exception
*/
public function destroy(Request $request, $parentKey, $relatedKey = null)
{
try {
$this->startTransaction();
$result = $this->destroyWithTransaction($request, $parentKey, $relatedKey);
$this->commitTransaction();
return $result;
} catch (\Exception $exception) {
$this->rollbackTransactionAndRaise($exception);
}
}
/**
* Delete a relation resource.
*
* @param Request $request
* @param int|string $parentKey
* @param int|string|null $relatedKey
* @return Resource
* @throws Exception
*/
protected function destroyWithTransaction(Request $request, $parentKey, $relatedKey = null)
{
$parentQuery = $this->buildDestroyParentFetchQuery($request, $parentKey);
$parentEntity = $this->runDestroyParentFetchQuery($request, $parentQuery, $parentKey);
$softDeletes = $this->softDeletes($this->resolveResourceModelClass());
$forceDeletes = $this->forceDeletes($request, $softDeletes);
$requestedRelations = $this->relationsResolver->requestedRelations($request);
$query = $this->buildDestroyFetchQuery($request, $parentEntity, $requestedRelations, $softDeletes);
$entity = $this->runDestroyFetchQuery($request, $query, $parentEntity, $relatedKey);
if ($this->isResourceTrashed($entity, $softDeletes, $forceDeletes)) {
abort(404);
}
$this->authorize($this->resolveAbility($forceDeletes ? 'forceDelete' : 'delete'), [$entity, $parentEntity]);
$beforeHookResult = $this->beforeDestroy($request, $parentEntity, $entity);
if ($this->hookResponds($beforeHookResult)) {
return $beforeHookResult;
}
if (!$forceDeletes) {
$this->performDestroy($entity);
if ($softDeletes) {
$entity = $this->runDestroyFetchQuery($request, $query, $parentEntity, $relatedKey);
}
} else {
$this->performForceDestroy($entity);
}
$entity = $this->cleanupEntity($entity);
if (count($this->getPivotJson())) {
$entity = $this->castPivotJsonFields($entity);
}
$afterHookResult = $this->afterDestroy($request, $parentEntity, $entity);
if ($this->hookResponds($afterHookResult)) {
return $afterHookResult;
}
$entity = $this->relationsResolver->guardRelations($entity, $requestedRelations);
return $this->entityResponse($entity);
}
/**
* Builds Eloquent query for fetching parent entity in destroy method.
*
* @param Request $request
* @param string|int $parentKey
* @return Builder
*/
protected function buildDestroyParentFetchQuery(Request $request, $parentKey): Builder
{
return $this->buildParentFetchQuery($request, $parentKey);
}
/**
* Runs the given query for fetching parent entity in destroy method.
*
* @param Request $request
* @param Builder $query
* @param string|int $parentKey
* @return Model
*/
protected function runDestroyParentFetchQuery(Request $request, Builder $query, $parentKey): Model
{
return $this->runParentFetchQuery($request, $query, $parentKey);
}
/**
* Builds Eloquent query for fetching relation entity in destroy method.
*
* @param Request $request
* @param Model $parentEntity
* @param array $requestedRelations
* @param bool $softDeletes
* @return Relation
*/
protected function buildDestroyFetchQuery(
Request $request,
Model $parentEntity,
array $requestedRelations,
bool $softDeletes
): Relation {
return $this->buildRelationFetchQuery($request, $parentEntity, $requestedRelations)
->when(
$softDeletes,
function ($query) {
$query->withTrashed();
}
);
}
/**
* Runs the given query for fetching relation entity in destroy method.
*
* @param Request $request
* @param Relation $query
* @param Model $parentEntity
* @param string|int $relatedKey
* @return Model
*/
protected function runDestroyFetchQuery(Request $request, Relation $query, Model $parentEntity, $relatedKey): Model
{
return $this->runRelationFetchQuery($request, $query, $parentEntity, $relatedKey);
}
/**
* The hook is executed before deleting a relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function beforeDestroy(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* Deletes or trashes the given relation entity from database.
*
* @param Model $entity
* @throws Exception
*/
protected function performDestroy(Model $entity): void
{
$entity->delete();
}
/**
* Deletes the given relation entity from database, even if it is soft deletable.
*
* @param Model $entity
*/
protected function performForceDestroy(Model $entity): void
{
$entity->forceDelete();
}
/**
* The hook is executed after deleting a relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function afterDestroy(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* Restores a previously deleted relation resource in a transaction-save way.
*
* @param Request $request
* @param int|string $parentKey
* @param int|string|null $relatedKey
* @return Resource
*/
public function restore(Request $request, $parentKey, $relatedKey = null)
{
try {
$this->startTransaction();
$result = $this->restoreWithTransaction($request, $parentKey, $relatedKey);
$this->commitTransaction();
return $result;
} catch (\Exception $exception) {
$this->rollbackTransactionAndRaise($exception);
}
}
/**
* Restores a previously deleted relation resource.
*
* @param Request $request
* @param int|string $parentKey
* @param int|string|null $relatedKey
* @return Resource
*/
protected function restoreWithTransaction(Request $request, $parentKey, $relatedKey = null)
{
$parentQuery = $this->buildRestoreParentFetchQuery($request, $parentKey);
$parentEntity = $this->runRestoreParentFetchQuery($request, $parentQuery, $parentKey);
$requestedRelations = $this->relationsResolver->requestedRelations($request);
$query = $this->buildRestoreFetchQuery($request, $parentEntity, $requestedRelations);
$entity = $this->runRestoreFetchQuery($request, $query, $parentEntity, $relatedKey);
$this->authorize($this->resolveAbility('restore'), [$entity, $parentEntity]);
$beforeHookResult = $this->beforeRestore($request, $parentEntity, $entity);
if ($this->hookResponds($beforeHookResult)) {
return $beforeHookResult;
}
$this->performRestore($entity);
$entity = $this->runRestoreFetchQuery($request, $query, $parentEntity, $relatedKey);
$entity = $this->cleanupEntity($entity);
if (count($this->getPivotJson())) {
$entity = $this->castPivotJsonFields($entity);
}
$afterHookResult = $this->afterRestore($request, $parentEntity, $entity);
if ($this->hookResponds($afterHookResult)) {
return $afterHookResult;
}
$entity = $this->relationsResolver->guardRelations($entity, $requestedRelations);
return $this->entityResponse($entity);
}
/**
* Builds Eloquent query for fetching parent entity in restore method.
*
* @param Request $request
* @param string|int $parentKey
* @return Builder
*/
protected function buildRestoreParentFetchQuery(Request $request, $parentKey): Builder
{
return $this->buildParentFetchQuery($request, $parentKey);
}
/**
* Runs the given query for fetching parent entity in restore method.
*
* @param Request $request
* @param Builder $query
* @param string|int $parentKey
* @return Model
*/
protected function runRestoreParentFetchQuery(Request $request, Builder $query, $parentKey): Model
{
return $this->runParentFetchQuery($request, $query, $parentKey);
}
/**
* Builds Eloquent query for fetching relation entity in restore method.
*
* @param Request $request
* @param Model $parentEntity
* @param array $requestedRelations
* @return Relation
*/
protected function buildRestoreFetchQuery(
Request $request,
Model $parentEntity,
array $requestedRelations
): Relation {
return $this->buildRelationFetchQuery($request, $parentEntity, $requestedRelations)
->withTrashed();
}
/**
* Runs the given query for fetching relation entity in restore method.
*
* @param Request $request
* @param Relation $query
* @param Model $parentEntity
* @param string|int $relatedKey
* @return Model
*/
protected function runRestoreFetchQuery(Request $request, Relation $query, Model $parentEntity, $relatedKey): Model
{
return $this->runRelationFetchQuery($request, $query, $parentEntity, $relatedKey);
}
/**
* The hook is executed before restoring a relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function beforeRestore(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* Restores the given relation entity.
*
* @param Model|SoftDeletes $entity
*/
protected function performRestore(Model $entity): void
{
$entity->restore();
}
/**
* The hook is executed after restoring a relation resource.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @return mixed
*/
protected function afterRestore(Request $request, Model $parentEntity, Model $entity)
{
return null;
}
/**
* Fills attributes on the given relation entity.
*
* @param Request $request
* @param Model $parentEntity
* @param Model $entity
* @param array $attributes
* @param array $pivot
*/
protected function performFill(
Request $request,
Model $parentEntity,
Model $entity,
array $attributes,
array $pivot
): void {
$entity->fill(
Arr::except($attributes, array_keys($entity->getDirty()))
);
}
/**
* @param Request $request
* @param Model $parentEntity
* @param array $filterDescriptor
* @return array
*/
protected function beforeFilterApplied(Request $request, Model $parentEntity, array $filterDescriptor): array
{
return $filterDescriptor;
}
}
Function Calls
None |
Stats
MD5 | ced233b222549b540c11ac8444d58093 |
Eval Count | 0 |
Decode Time | 100 ms |