Created
January 17, 2025 16:14
-
-
Save Alexisgt01/bd319825fc6626f6b17e562e9661e359 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Services\Support; | |
use Illuminate\Database\Eloquent\Relations\Relation; | |
use Illuminate\Support\Arr; | |
final readonly class AutoEagerLoadEntity | |
{ | |
public function __construct( | |
private \Closure $access, | |
private \Closure $filter, | |
private array $routes = ['*'], | |
) | |
{ | |
} | |
/** | |
* @param ...$args | |
* @return Relation | |
*/ | |
public function callFilter(...$args): Relation | |
{ | |
/** @var Relation $relation */ | |
$relation = Arr::first($args); | |
return is_null(call_user_func_array($this->filter, $args)) ? $relation : call_user_func_array($this->filter, $args); | |
} | |
/** | |
* @param ...$args | |
* @return bool | |
*/ | |
public function callAccess(...$args): bool | |
{ | |
return is_null(call_user_func_array($this->access, $args)) ? false : call_user_func_array($this->access, $args); | |
} | |
/** | |
* @return \Closure | |
*/ | |
public function getAccess(): \Closure | |
{ | |
return $this->access; | |
} | |
/** | |
* @return \Closure | |
*/ | |
public function getFilter(): \Closure | |
{ | |
return $this->filter; | |
} | |
/** | |
* @return array|string[] | |
*/ | |
public function getRoutes(): array | |
{ | |
return $this->routes; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Services\Support; | |
use Illuminate\Database\Eloquent\Builder; | |
use Illuminate\Database\Eloquent\Collection; | |
use Illuminate\Database\Eloquent\Model; | |
use Illuminate\Database\Eloquent\Relations\Relation; | |
use Illuminate\Http\Request; | |
use Illuminate\Pagination\LengthAwarePaginator; | |
use Illuminate\Support\Arr; | |
class AutoEagerLoadFactory | |
{ | |
public array $factories = []; | |
public function __construct() | |
{ | |
} | |
public function load(LengthAwarePaginator|Collection|Builder|Model &$query, array $relations = [], ?Request $request = null): void | |
{ | |
if ($query instanceof LengthAwarePaginator || $query instanceof Collection) { | |
$model = $query->first(); | |
if (!$model) return; | |
} | |
$model = $model ?? $query->getModel(); | |
$loads = []; | |
foreach ($relations as $relation) { | |
if (!$this->hasEntity($model::class, $relation)) { | |
continue; | |
} | |
$entity = $this->getEntity($model::class, $relation); | |
if (!$entity->callAccess($model, $request)) { | |
continue; | |
} | |
if (is_null($request)) { | |
$loads[$relation] = fn(Relation $queryRelation) => $entity->callFilter($queryRelation, $request); | |
continue; | |
} | |
$controllerMethod = $request->route()->getActionMethod(); | |
if(in_array('*', $entity->getRoutes()) || in_array($controllerMethod, $entity->getRoutes())) { | |
$loads[$relation] = fn(Relation $queryRelation) => $entity->callFilter($queryRelation, $request); | |
} | |
} | |
$query instanceof Builder ? $query->with($loads) : $query->load($loads); | |
} | |
/** | |
* @param string $className | |
* @param string $relation | |
* @return AutoEagerLoadEntity | |
*/ | |
public function getEntity(string $className, string $relation): AutoEagerLoadEntity | |
{ | |
if ($factory = | |
Arr::get($this->factories, $className)[$relation] | |
) return $factory; | |
throw new \InvalidArgumentException("Combination of $className and $relation is not a valid entity. Please register it with 'AutoEagerLoad::allow()' method inside any appropriate service provider."); | |
} | |
/** | |
* @param string $className | |
* @param string $relation | |
* @param AutoEagerLoadEntity $entity | |
* @return $this | |
*/ | |
public function allow(string $className, string $relation, AutoEagerLoadEntity $entity): self | |
{ | |
$this->factories[$className][$relation] = $entity; | |
return $this; | |
} | |
/** | |
* @param string $className | |
* @param array $values : key -> value where key = relation and value = AutoEagerLoadEntity instance | |
* @return self | |
*/ | |
public function allows(string $className, array $values): self | |
{ | |
foreach ($values as $relation => $entity) { | |
$this->allow($className, $relation, $entity); | |
} | |
return $this; | |
} | |
/** | |
* @param string $className | |
* @param string $relation | |
* @return bool | |
*/ | |
public function hasEntity(string $className, string $relation): bool | |
{ | |
return !is_null(Arr::get($this->factories, "$className.$relation")); | |
} | |
/** | |
* @return array | |
*/ | |
public function getFactories(): array | |
{ | |
return $this->factories; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment