Laravel: рекурсивный дочерний фильтр дерева - PullRequest
2 голосов
/ 12 марта 2020

Я не могу применить фильтрацию ко всем дочерним элементам, определенным в древовидном формате модели с механизмом активной загрузки

Вот мое определение модели (прекрасно работает):

class Section extends Model
{
    [...]
    /**
     * @return HasOne
     */
    public function parent()
    {
        return $this->hasOne(
            self::class,
            'Id',
            'IdParent'
        )->with('parent');
    }

    /**
     * @return HasMany
     */
    public function children()
    {
        return $this->hasMany(
            self::class,
            'IdParent',
            'Id'
        )->with('children');
    }
    [...]
}

Теперь я хочу отфильтровать рекурсив на основе «объекта критериев»

public function getMachines(SectionCriteria $sectionCriteria = NULL)
{
    /**
     * @var $builder Builder|Section
     */
    $builder = Section::with([
        'children' => function ($query) use ($sectionCriteria) {
            if ($sectionCriteria) {
                foreach ($sectionCriteria->getFilterFlags() as $flagName => $flagValue) {
                    if ($flagValue) {
                        $query->whereFlag($flagName);  //Custom implementation
                    } else {
                        $query->whereNotFlag($flagName); //Custom implementation
                    }
                }
            }
        }
    ]);

Этот бот работает, он применяется к первому уровню дерева.

Мой вопрос будет : есть ли способ передать объект в отношение children(), чтобы я мог применять рекурсивные фильтры (которые будут применяться ко всем уровням)?

Что-то вроде, скажем:

PS: это невозможно, поскольку в качестве параметра принимается только обратный вызов

public function children($parameters)
{
    return $this->hasMany(
        self::class,
        'IdParent',
        'Id'
    )->with('children'=>$parameters);
}

То, что я не хотел бы использовать (в отношении SOLID принципы):

  1. Создание переменной класса stati c, которая содержит критерии
  2. Глобальная переменная любого вида

1 Ответ

0 голосов
/ 17 марта 2020

Я также пытался получить дочерние рекурсивные (и применить фильтры), но в итоге получил больше запросов, поэтому Eloquent хорошо оптимизировал sooo ...

Я использовал технику № 1 (Создайте класс c) переменная) хотя мне это не очень нравится, но она работает.

Модель

/**
 * @var null|SectionCriteria
 */
public static $childrenFilter = NULL;  //This can be whatever you need since it's static


/**
 * @return HasMany
 */
public function children()
{
    return $this->hasMany(
        self::class,
        'IdParent',
        'Id'
    )->with(['children'=>self::searchChild()]);
}

/**
 * @return \Closure
 */
public function searchChild()
{
    return function ($builder) {
        if (Section::$childrenFilter) {
           foreach ($sectionCriteria->getFilterFlags() as $flagName => $flagValue) {
                if ($flagValue) {
                    $query->whereFlag($flagName);  //Custom implementation
                } else {
                    $query->whereNotFlag($flagName); //Custom implementation
                }
            }
        }
    };
}

/**
 * @param SectionCriteria $criteria
 */
public static function setChildSearch(SectionCriteria $criteria)
{
    Section::$childrenFilter = $criteria;
}

/**
 * Remove the search criteria filter
 */
public static function clearChildSearch()
{
    Section::$childrenFilter = NULL;
}

Репозиторий (фактическое использование)

/**
 * @param SectionCriteria|NULL $sectionCriteria
 * @return Section[]|Collection
 */
public function getMachines(SectionCriteria $sectionCriteria = NULL)
{
    /**
     * @var $builder Builder|Section
     */
    $builder = Section::with(['children']); //Here I do not need the root elements to be filtered, If needed then use: Section::with(['children'=>Section::searchChild()])
    Section::setChildSearch($sectionCriteria);
    $builder->orderBy('Name');
    $results = $builder->get();
    Section::clearChildSearch();
    return $results;
}

Опять же ... не preety, но он выполняет свою работу

Новое : Другой способ (проверит это) - расширить класс Builder

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...