Skip to content

Instantly share code, notes, and snippets.

@Alexisgt01
Created January 17, 2025 16:14
Show Gist options
  • Save Alexisgt01/bd319825fc6626f6b17e562e9661e359 to your computer and use it in GitHub Desktop.
Save Alexisgt01/bd319825fc6626f6b17e562e9661e359 to your computer and use it in GitHub Desktop.
<?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;
}
}
<?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