Skip to content

Instantly share code, notes, and snippets.

@lesstif
Last active January 2, 2020 05:01
Show Gist options
  • Save lesstif/1ec531c38137744a1d2c7931729f0ca4 to your computer and use it in GitHub Desktop.
Save lesstif/1ec531c38137744a1d2c7931729f0ca4 to your computer and use it in GitHub Desktop.
laravel nova 의 attachable 이 relation 때문에 느릴때 사용하는 AttachableController 컨트롤러.
<?php
namespace Laravel\Nova\Http\Controllers;
use Laravel\Scout\Searchable;
use Laravel\Nova\Fields\Field;
use Laravel\Nova\TrashedStatus;
use Illuminate\Routing\Controller;
use Laravel\Nova\Fields\MorphToMany;
use Laravel\Nova\Fields\BelongsToMany;
use Laravel\Nova\Http\Requests\NovaRequest;
/**
* 과다한 relation 으로 attach 쿼리가 느려서 검색엔진 사용하도록 수정한 컨트롤러
*
* Class AttachableController
* @package Laravel\Nova\Http\Controllers
*
* cp laravel-nova-AttachableController.php vendor/laravel/nova/src/Http/Controllers/AttachableController.php
*/
class AttachableController extends Controller
{
public function index(NovaRequest $request)
{
return $this->indexNew($request);
}
/**
* List the available related resources for a given resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return \Illuminate\Http\Response
*/
public function indexNew(NovaRequest $request)
{
$field = $request->newResource()
->availableFields($request)
->filter(function ($field) {
return $field instanceof BelongsToMany || $field instanceof MorphToMany;
})
->firstWhere('resourceName', $request->field);
$withTrashed = $this->shouldIncludeTrashed(
$request,
$associatedResource = $field->resourceClass
);
// get ids that is already attached
$keys = $this->getAttachedKeys($request, $field);
// get model class name
$model = $field->resourceClass::${'model'};
if ($this->isClassSearchable($model)) {
$class = new $model();
// TODO withTrashed 옵션 추가(->whereNotNull('deleted_at'))
$records = $class->search($request->search)->paginate(500);
$resources = $records->map(function ($item, $key) use ($keys) {
$title = $item->title ?? null;
if ($title === null) {
$title = $item->name ?? null;
}
return [
'display' => $key . 'th (' . $item->id . ' => ' . $title . ')',
'value' => $item->id,
];
})->reject(function ($item) use($keys){
// 이미 추가된 element 제외
if (in_array($item['value'], $keys)) {
return true;
}
return false;
})->sortBy('value');
$softDeletes = null;
} else {
$parentResource = $request->findResourceOrFail();
$resources = $field->buildAttachableQuery($request, $withTrashed)->get()
->mapInto($field->resourceClass)
->filter(function ($resource) use ($request, $parentResource) {
return $parentResource->authorizedToAttach($request, $resource->resource);
})
->map(function ($resource) use ($request, $field) {
return $field->formatAttachableResource($request, $resource);
})
->reject(function ($resource) use ($keys) {
return in_array($resource['value'], $keys);
})->sortBy('display', SORT_NATURAL | SORT_FLAG_CASE)->values();
$softDeletes = $associatedResource::softDeletes();
}
return compact(['resources', 'withTrashed', 'softDeletes']);
}
/**
* List the available related resources for a given resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return \Illuminate\Http\Response
*/
public function indexOrg(NovaRequest $request)
{
$field = $request->newResource()
->availableFields($request)
->filter(function ($field) {
return $field instanceof BelongsToMany || $field instanceof MorphToMany;
})
->firstWhere('resourceName', $request->field);
$withTrashed = $this->shouldIncludeTrashed(
$request,
$associatedResource = $field->resourceClass
);
$parentResource = $request->findResourceOrFail();
$ret = [
'resources' => $field->buildAttachableQuery($request, $withTrashed)->get()
->mapInto($field->resourceClass)
->filter(function ($resource) use ($request, $parentResource) {
return $parentResource->authorizedToAttach($request, $resource->resource);
})
->map(function ($resource) use ($request, $field) {
return $field->formatAttachableResource($request, $resource);
})->sortBy('display', SORT_NATURAL | SORT_FLAG_CASE)->values(),
'withTrashed' => $withTrashed,
'softDeletes' => $associatedResource::softDeletes(),
];
return $ret;
}
/**
* Determine if the query should include trashed models.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param string $associatedResource
* @return bool
*/
protected function shouldIncludeTrashed(NovaRequest $request, $associatedResource)
{
if ($request->withTrashed === 'true') {
return true;
}
$associatedModel = $associatedResource::newModel();
if ($request->current && $associatedResource::softDeletes()) {
$associatedModel = $associatedModel->newQueryWithoutScopes()->find($request->current);
return $associatedModel ? $associatedModel->trashed() : false;
}
return false;
}
protected function isClassSearchable($fqn)
{
if (!in_array(Searchable::class, class_uses_recursive($fqn), true)) {
return false;
}
return true;
}
/**
* MorphTo relation 시 이미 추가된 model id 가져오기
*
* @param NovaRequest $request
* @param Field $field
* @return mixed
*/
protected function getAttachedKeys(NovaRequest $request, Field $field)
{
$model = $request->findModelOrFail();
$key = $field->resourceClass::newModel()->getQualifiedKeyName();
return $model->{$field->attribute}()->select($key)->pluck($key)->toArray();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment