Как сделать атрибут модели Eloquent обновляемым только с помощью общедоступных методов?

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

class Person extends Model
{
    public function addMoney($amount)
    {
        if ($amount <= 0) {
            throw new Exception('Invalid amount');
        }
        $this->money += $amount;
    }

    public function useMoney($amount)
    {
        if ($amount > $this->money) {
            throw new Exception('Invalid funds');
        }
        $this->money -= $amount;
    }
}

Нельзя допускать:

$person->money = -500;

Вы должны использовать какой-то метод доступа или установки:

$person->useMoney(100);

Но мне все равно, как вы получите значение:

echo $person->money;
// or
echo $person->getMoney();
// whatever

Как сделать так, чтобы единственный способ обновить этот атрибут — использовать определенные методы, которые диктуют некоторую дополнительную логику? В некотором смысле сделать атрибут модели закрытым или защищенным.

Я хочу сделать это отдельно и/или до того, как данные модели будут сохранены в базе данных.


person BadHorsie    schedule 29.06.2020    source источник
comment
Я не думаю, что это правильное место для добавления такого рода проверки. Вместо этого я думаю, что использование валидатора для проверки пользовательского ввода перед внесением изменений в модель было бы правильным подходом.   -  person fubar    schedule 29.06.2020


Ответы (2)


Вы можете переопределить функцию set..Attribute() для каждой переменной-члена, которую хотите защитить, или потенциально можете выполнять проверку внутри функции set..Attribute(), а не использовать отдельные общедоступные методы.

class Person extends Model
{
    public function addMoney($amount)
    {
        if ($amount <= 0) {
            throw new Exception('Invalid amount');
        }

        if (!isset($this->attributes['money'])) {
            $this->attributes['money'] = $amount;
        } else {
            $this->attributes['money'] += $amount;
        }
    }

    public function useMoney($amount)
    {
        if ($amount > $this->money) {
            throw new Exception('Invalid funds');
        }

        if (!isset($this->attributes['money'])) {
            $this->attributes['money'] = -$amount;
        } else {
            $this->attributes['money'] -= $amount;
        }
    }

    public function setMoneyAttribute($val) {
        throw new \Exception('Do not access ->money directly, See addMoney()');
    }

}
person bumperbox    schedule 29.06.2020
comment
Я уже пробовал что-то подобное, но сам класс не может изменить атрибуты, если вы переопределите setAttribute(). - person BadHorsie; 30.06.2020
comment
хороший момент, я обновил ответ, так что он будет работать. путем прямой ссылки на атрибуты при использовании внутри класса через поле $attributes - person bumperbox; 30.06.2020
comment
Я тоже так пробовал. Это просто дало мне исключение Undefined index для атрибута. - person BadHorsie; 30.06.2020
comment
Спасибо, это работает. Не уверен, почему я предположил, что не могу настроить атрибут вручную. Все еще не уверен, что мне действительно нравится моя первоначальная идея для этого вопроса, но ваше решение действительно работает. - person BadHorsie; 02.07.2020
comment
Да, это кажется немного нелогичным по отношению к тому, как обычно работают модели. Вы можете создать отдельный класс, который манипулирует моделью и выполняет там проверку. Например, $money_service-›useMoney($model, $amount), хотя, в конце концов, лучше всего подойдет то, что проще и легче всего понять. - person bumperbox; 02.07.2020

Используйте мутатор, и ваш код должен выглядеть так:

class Person extends Model
{
    public function setMoneyAttribute($amount)
    {
        if ($amount < 0) {
            throw new Exception('Invalid amount');
        }
        $this->attributes['money'] = $amount;
        $this->save();
    }

   public function addMoney($amount)
    {
        if ($amount <= 0) {
            throw new Exception('Invalid amount');
        }
        $this->money += $amount;
    }

    public function useMoney($amount)
    {
        if ($amount > $this->money) {
            throw new Exception('Invalid funds');
        }
        $this->money -= $amount;
    }
}

Теперь вы можете использовать $person-›money = -500, и это вызовет исключение. Надеюсь это поможет.

person Sajal hossain    schedule 29.06.2020