Разбираемся с protected , $fillable  и #[Fillable]  в Laravel

Если вы изучаете Laravel, вы могли заметитькое-что необычное в новых моделях Laravel:

#[Fillable(['name', 'email', 'password'])]
#[Hidden(['password', 'remember_token'])]
class User extends Authenticatable
{
//
}

Если вы изучали традиционный подход в Laravel, вы можете задаться вопросом:

Что стало с protected $fillable  и protected $hidden ?

И что более важно:

Является ли #[Fillable]  свойством? Можно ли сделать его protected ?

Чтобы полностью в этом разобраться, нам нужно разделить три концепции PHP:

  1. Свойства (Properties)
  2. Область видимости (Visibility)
  3. Атрибуты (Attributes)

Давайте начнём с самого начала.

1. Что такое свойство в PHP?

Свойство — это, по сути, переменная, принадлежащая классу или объекту.

Например:

class User
{
public $name;
public $email;
}

Здесь $name  и $email  — это свойства класса User .

Мы можем создать объект:

$user = new User();
$user->name = "Aditya";
$user->email = "aditya@example.com";

Теперь объект содержит данные:

User object

├── name → "Aditya"
└── email → "aditya@example.com"

Так что когда вы видите:

$user->name

name  — это свойство, принадлежащее объекту $user .

2. Свойства имеют область видимости

PHP позволяет нам контролировать, кто может получать доступ к свойству.

Существует три основных модификатора видимости:

public
protected
private

Например:

class User
{
public $name;
protected $email;
private $password;
}

У этих трёх свойств разные правила доступа.

public 

К публичному свойству можно обратиться откуда угодно.

$user = new User();
$user->name = "Aditya";
echo $user->name;

Это работает, потому что $name  — публичное свойство.

protected 

К защищённому свойству можно обратиться внутри самого класса и в его классах-потомках.

class User
{
protected $email;
}

Вы не можете напрямую обратиться к нему извне:

$user = new User();
$user->email = "aditya@example.com";

Это приведёт к ошибке доступа, так как $email  имеет модификатор protected.

private 

К приватному свойству можно обратиться только из того класса, в котором оно объявлено.

class User
{
private $password;
}

Вне класса:

$user->password = "secret";

так сделать нельзя.

3. Что означает protected $fillable ?

Теперь давайте посмотрим на Laravel.

Традиционная модель User  в Laravel может содержать:

class User extends Authenticatable
{
protected $fillable = [
'name',
'email',
'password',
];
}

Давайте разберём это по частям.

protected

— это модификатор видимости.

$fillable

— это имя свойства.

[
'name',
'email',
'password'
]

— это значение, хранящееся в этом свойстве.

Таким образом:

protected $fillable = [
'name',
'email',
'password',
];

означает:

Создать защищённое свойство с именем $fillable , содержащее эти три значения.

4. Зачем Laravel нужен $fillable ?

Laravel использует $fillable  для контроля массового заполнения (mass assignment).

Например:

$user = User::create([
'name' => 'Aditya',
'email' => 'aditya@example.com',
'password' => 'secret',
]);

Laravel должен знать, какие поля разрешено заполнять таким способом.

Для этого и нужен $fillable :

protected $fillable = [
'name',
'email',
'password',
];

Он говорит Laravel:

Эти атрибуты разрешены для массового заполнения.

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

5. Так что же такое #[Fillable(...)] ?

Теперь перейдём к более новому синтаксису:

#[Fillable(['name', 'email', 'password'])]

Это выглядит похоже на $fillable , но принципиально отличается от него.

Это не свойство.

Это атрибут PHP.

PHP представил атрибуты в версии PHP 8.

Синтаксис #[]  обозначает атрибут:

#[SomeAttribute]

Атрибут можно представить как метаданные или дополнительные инструкции, прикреплённые к PHP-коду.

Например:

#[Fillable(['name', 'email', 'password'])]
class User extends Authenticatable
{
}

Атрибут Fillable  прикреплён к классу User .

Laravel затем может считать эту информацию и использовать её для конфигурации модели.

6. Почему нельзя написать protected #[Fillable(...)] ?

Именно здесь разница становится существенной.

Вы можете подумать:

protected #[Fillable(...)]

Но атрибуты PHP работают иначе.

Почему?

Потому что:

protected $fillable;

— это объявление свойства.

В то время как:

#[Fillable(...)]

— это атрибут.

Это разные языковые конструкции.

Представьте это так:

protected $fillable
│ │
│ └── Property

└── Visibility

В то время как:

#[Fillable(...)]

└── Attribute / Metadata

К самому атрибуту не привязан модификатор protected .

7. Атрибуты можно вешать на свойства

Вот здесь начинается самое интересное.

Атрибуты PHP на самом деле можно прикрепить к разным элементам кода, включая свойства.

Например:

class User
{
#[SomeAttribute]
public string $name;
}

Здесь:

public string $name;

всё ещё является свойством.

А:

#[SomeAttribute]

— это метаданные, прикреплённые к этому свойству.

Таким образом, эти концепции могут сосуществовать:

class User
{
#[SomeAttribute]
protected string $name;
}

Здесь:

  • protected  → область видимости
  • $name  → свойство
  • string  → тип свойства
  • #[SomeAttribute]  → атрибут

8. Простая ментальная модель

Представьте автомобиль.

Свойства описывают то, что есть у автомобиля:

class Car
{
public string $color;
protected int $speed;
}

У автомобиля есть:

color
speed

Это свойства.

Область видимости говорит нам, кто может получить к ним доступ:

public    → everyone
protected → class + children
private → class only

Теперь представьте, что вы наклеиваете на машину шильдик:

#[Electric]
class Car
{
}

#[Electric]  не является свойством вроде $color .

Это метаданные, сообщающие PHP или фреймворку информацию о классе.

9. Традиционный Laravel против Laravel на атрибутах

Традиционный Laravel:

class User extends Authenticatable
{
protected $fillable = [
'name',
'email',
'password',
];
    protected $hidden = [
'password',
'remember_token',
];
}

Здесь у нас есть два свойства:

$fillable
$hidden

Оба они являются protected.

При подходе с использованием атрибутов:

#[Fillable(['name', 'email', 'password'])]
#[Hidden(['password', 'remember_token'])]
class User extends Authenticatable
{
}

Теперь у нас нет свойств $fillable  и $hidden , прописанных в классе.

Вместо этого мы прикрепляем атрибуты Laravel к классу.

10. Не путайте $fillable  с Fillable 

Эта крошечная разница чрезвычайно важна.

$fillable 

protected $fillable = [
'name',
'email',
];

$fillable  — это свойство.

Fillable 

#[Fillable([
'name',
'email',
])]

Fillable  — это класс атрибута.

Заметьте разницу:

$fillable

Property

против:

Fillable

Attribute

Символ $  — полезная подсказка, указывающая на то, что вы имеете дело с переменной или свойством.

11. Как насчёт методов?

Та же концепция применима и к методам.

Метод — это действие:

class User
{
public function login()
{
// ...
}
}

Вы также можете прикрепить атрибут к методу:

class User
{
#[SomeAttribute]
public function login()
{
// ...
}
}

Здесь:

public

управляет видимостью.

login()

— сам метод.

#[SomeAttribute]

— метаданные, прикреплённые к методу.

И снова: это разные концепции.

12. Три вещи, которые стоит запомнить

Читая код на PHP/Laravel, мысленно разделяйте следующее:

Свойство (Property)

protected $fillable;

Свойство хранит данные, принадлежащие объекту.

Область видимости (Visibility)

public
protected
private

Область видимости определяет, кто имеет доступ к свойству или методу.

Атрибут (Attribute)

#[Fillable(...)]

Атрибут предоставляет метаданные/инструкции, которые могут считывать PHP или фреймворк.

13. Финальный пример

Собрём всё вместе:

#[Fillable(['name', 'email', 'password'])]
#[Hidden(['password', 'remember_token'])]
class User extends Authenticatable
{
protected string $email;
private string $password;
public function login()
{
// Login logic
}
}

Теперь определим каждую часть:

#[Fillable(...)]

Attribute

#[Hidden(...)]

Attribute

protected string $email
│ │ │
│ │ └── Property
│ └────────── Type
└──────────────────── Visibility

private string $password
│ │ │
│ │ └── Property
│ └────────── Type
└────────────────── Visibility

public function login()
│ │
│ └── Method
└───────── Visibility

Как только вы поймёте это различие, новый синтаксис Laravel станет гораздо понятнее.

Золотое правило

Каждый раз, когда вы видите:

public
protected
private

думайте:

Кому разрешён доступ к этому?

Каждый раз, когда вы видите:

$something

внутри класса, думайте:

Скорее всего, это свойство.

Каждый раз, когда вы видите:

#[Something(...)]

думайте:

Это атрибут — метаданные/инструкции, прикреплённые к PHP-коду.

Эти три концепции связаны, но они не взаимозаменяемы.

Понимание этого различия — одна из тех баз PHP, которая значительно облегчает чтение современного кода на Laravel.