Laravel模型:模型属性在哪里?

29

(我来自Visual Studio + Entity Framework背景,正在尝试在Laravel + Eloquent中找到相应的功能)

在EF和Visual Studio中,我们向应用程序添加一个新模型,然后告诉它有关我们现有数据库的信息。然后,EF可以为我的表生成具有公共列属性的模型。这为我们提供了所有IDE和编译器的好处,例如Intellisense、拼写错误检测等。

最近我开始探索VS Code、Laravel和Eloquent。通过所有这些教程和文章,我不确定这些属性是何时以及如何在模型类中生成的。我刚刚尝试了artisan make:model命令,它确实生成了模型类,但其中没有属性。所以,

  1. 我应该手动编写它们吗?(真的吗?)
  2. 这些只是公共变量还是带有getter/setter的标准属性(请原谅我对.NET的心态:))?
  3. 是否有工具/扩展程序可以检查我的数据库并为其列创建具有属性的模型?

更新

感谢回答我的问题的人们。我发表的一些评论是因为我对PHP关于成员访问的(在我看来很奇怪的)方法的无知;我刚刚发现,PHP不会抱怨不存在的类成员,而是在运行时生成它们(例如$post->NonExistingMember = SomeValue可以正常运行;在我所知道的大多数其他语言中,这甚至不能编译)。这对我来说是个大惊喜。我使用过C++、VB、C#、Java等多种语言,在任何其他地方都没有见过这种行为。所有这些语言都会立即抛出编译时错误,比如类型X不包含名为Y的成员。看不出PHP的不同方法如何与OOP相适应。
我发布这个问题的实际问题仍然没有解决。虽然我可以使用reliese/laravel为我的数据库生成模型类,但该工具仍然不会根据表列生成类成员,因此我无法获得自动完成的好处。我希望从专家那里听到是否可以做到这一点(当然是自动的)。
更新2
现在我对Laravel环境有了一点了解,我想分享一下我的经验。请参见下面的答案。

也许这个链接可以帮到你: https://dev59.com/e10a5IYBdhLWcg3wPGiQ - Rafael
也许这可以帮助你:Laravel: Eloquent 入门。但基本上,是的,你需要手写它们。有一些可用的软件包可以从现有数据库生成模型。 - brombeer
@LouiseEggleton:我花了更多时间使用Laravel。基本问题(或称之为特性)是,一个类在编译时不需要包含成员,因为没有所谓的编译时。PHP在运行时解释代码并动态生成成员。因此,模型类根本不需要任何成员。这与C#世界非常不同,我们有强类型模型(实体甚至数据集),类属性和底层表列之间存在1对1的对应关系。所有这些意味着,在VS Code中不会有自动完成的功能来支持Laravel。 - dotNET
@LouiseEggleton:有一个很好的小工具可以从你的数据库创建模型类(请参见上面 Rafael 的评论),但即使是那个工具也只会生成 DocBlocks(注释),而不是实际的类成员。如果必须的话,你可以手写它们,但是在使用它一段时间后,我认为这是不必要的。 - dotNET
但是显式属性至少能帮助 IntelliSense 并显示意图,不是吗? - Louise Eggleton
显示剩余6条评论
6个回答

29

现在我已经花了一些时间来学习Laravel、Eloquent和PHP,我想分享一些内容,希望能够帮助其他初学者。

PHP是一种动态语言,它的代码是即时编译的(与C#和VB.NET不同)。您的模型类不需要显式定义成员才能访问或分配成员,只要它们扩展自Model(Laravel Eloquent中的内置类),您就可以将值分配给与底层数据库表列相同名称的不存在的成员,并且Eloquent将为您存储它在数据库中。例如,如果您的数据库中有一个名为posts的表,其中包含一个名为body的列,您可以编写以下代码在数据库中创建新记录:

$p = new Post;
$p->body = 'Some stuff';
$p->save();

当然,你需要在项目中有一个名为Post的类,它继承自Model,但你不需要在这个类中定义一个成员body。对于来自.NET世界的人们来说,这可能听起来很奇怪,但这就是动态语言的工作方式。
至于自动生成模型,Laravel包含了内置命令(php artisan make:model),可以为你生成模型。
最后,为了实现智能感知和自动完成,使用与Laravel自身相同的工具,即DocBlocks。这些是PHP中的一种特殊类型的注释,你可以使用它来记录你的代码元素,因此你可以为所有的模型类添加属性名称和类型的DocBlocks。幸运的是,VS Code中有一个非常好的扩展程序,可以自动为你完成这个过程。使用以下命令进行安装:
composer require --dev barryvdh/laravel-ide-helper

现在运行以下命令,为所有模型类生成文档块(显然,在此之前,您应该已经生成了数据库和模型):

php artisan ide-helper:models --dir='app'

这个扩展将获取您数据库的结构,并向所有模型注入DocBlocks,看起来像这样:

/**
 * App\User
 *
 * @property int $id
 * @property string $name
 * @property \Illuminate\Support\Carbon|null $created_at
 * @property \Illuminate\Support\Carbon|null $updated_at
 * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereCreatedAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereId($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereName($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Exam whereUpdatedAt($value)
 * @mixin \Eloquent
 */
class User extends Model
{
}

现在,VS Code将在模型属性中显示表字段名称,如下所示(请注意,当我们输入na...时,智能感知会从DocBlocks中带出name成员):

enter image description here 请注意,我还安装了Intelephense插件,但我不确定是否需要它才能使用自动完成功能。

编辑

动态属性已经在PHP 8.2中被弃用,并且据我所听,它们将在PHP 9.0中变为无效,这意味着Laravel模型将无法在未来版本中执行这些操作。

虽然我不是PHP大师,但我听说我们不需要惊慌。两件事情:首先,实现__get__set方法的对象仍将正常工作。其次,您(他们)还可以在模型类上使用#[AllowDynamicProperties]来允许动态属性。最后,他们可以重写模型生成器,将列名作为属性输出到模型类中。这个方法将是最好的,并将使PHP更接近C#世界的工作方式(恰恰是这篇文章的起点,哈哈)。


13
你不需要定义属性,但确实不能定义属性。定义它们会破坏Eloquent的假设。这是我不理解的一种设计选择,更让人烦恼的是,在Laravel文档页面中没有提到它。 - aross
5
当我从Symfony转到Laravel时,这个问题困扰了我。像您一样,我会编写多种语言,而且我更喜欢强类型。在PHP 7+中,你可以编写严格或宽松的代码。在Symfony中,我使用Doctrine ORM来处理数据库,它们被称为实体而不是模型,但它们只是表的编码版本,并使用存储库进行查询。您实际上要使用getter和setter声明属性。 Laravel使用魔术方法创建属性,我不太喜欢这个方法,但总体上我还是喜欢Laravel,所以我忍受了它。使用docblock方法还是可以的,这样可以平衡一下。 - MMMWeirdo
那么现在考虑到 PHP 8.2 中动态属性已被弃用(https://www.php.net/releases/8.2/en.php#deprecate_dynamic_properties),怎么办呢? 我在 Laravel 文档中没有找到任何相关内容。 - Boris N
1
模型属性不是动态属性。Laravel使用__get和__set魔术方法将数据库列映射到属性中,这是一个属性数组。 - Deivide

8
我使用注释来声明自动完成的所有属性(在PHPStorm中工作,不确定其他IDE是否适用)。
/**
 * @property $id
 * @property $microsoft_id
 * @property $name
 * @property $qualification
 * @property $company
 */
class ShopTenant extends Model
{
    public $connection = 'shop';
    public $table = 'tenants';
    protected $guarded = ['id'];
}

您可以使用以下方法获取表格的所有列,并将该列表粘贴/格式化到您的注释中:
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = N'tenants';

3
  1. 不幸的是,是的,你需要手动编写它们,但只有在需要更新这些属性的值时才需要,查看第2点($fillable数组)
  2. 您需要声明可以填充的属性:

例如,一个名为“Post”的模型用于数据库表“posts”,该表具有2个列“title”和“body”:

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{

 //not mandatory to declare the table name, Laravel will find it using reflection if you follow the naming standard
 protected $table = 'posts'; //not mandatory

 protected $fillable = ['title','body'];
}

在控制器中:

$newPost = new Post;
$newPost->title = 'A new title';
$newPost->body = 'A new body';
$newPost->save(); //only at this point the db is modified

如果您在数组或JSON响应中返回属性(集合中隐藏的属性也会显示),则可以隐藏它们:

protected $hidden = [
 'title',
];

同时,您还可以使用访问器在模型中注入新属性。

  1. 我不认为这样做行得通,您可以安装一些Laravel VS Code插件,使生活更轻松(例如:Blade代码片段) 您可以查看这篇文章

谢谢。您能在答案中包含您的模型类定义吗? - dotNET
请检查我的更新答案,我希望这就是你所要求的。如果你需要其他的东西,我可以更新我的答案。 - NicuVlad
现在你已经接近我所问的问题了。在上面的代码中,$newPost->title是什么?在你的类中没有这样的属性或公共变量。即使有,那个变量又如何映射到底层的表列呢? - dotNET
标题是您数据库中的“标题”列,如果您需要创建一个帖子并添加“标题”值,则需要将标题添加到“$fillable”数组中。 - NicuVlad
默认情况下,Laravel 也会创建一个“id”、“created_at”和“updated_at”,但在这个数组中,您只需要声明需要更改的属性。 - NicuVlad
显示剩余4条评论

1
实际上,您不需要指定属性,laravel会自动完成所有操作。当您创建一个模型时,laravel使用类名的复数形式(并且它有一个非常好的系统:post变为posts,activity变为activities等)来访问表格。因此,您可以使用空模型而不设置$table或$fillable/$guarded属性。
// use only, if your table name differs from the models classname
$table = 'users_options'; // when you want to name your model 'Vote' but the table is called 'users_options' for instance.

// use fillable and guarded only to specify mass-assignment columns
$fillable = [whatever, ...];
$guarded = [whatever, ...];

您可以随时访问这些属性:

class Post extends Model
{

}

// would do sth like this: select name from posts where id = 1;
// without specifying anything in the model
$name = Post::find(1)->name;

我感到非常困惑。在Post类中,您没有声明任何名为“name”的属性或变量。为什么会返回Post::find(1)->name的任何内容呢?这甚至可以编译吗?PHP应该立即抛出编译时错误,指出“name”不是Post类的成员。 - dotNET
请原谅我的无知。我刚发现 PHP 不会抱怨不存在的类成员,而是会在运行时动态生成它们。对我来说这是个大惊喜。我使用过 C++、VB、C#、Java 和其他几种语言,但从未在其他地方看到这种行为。所有这些语言都会立即抛出编译时错误,说什么“类型 X 不包含名为 Y 的成员”。看不出 PHP 的不同方法如何与 OOP 结合。 - dotNET

0

你需要自己添加。例如:

$fillable = ['firstname', 'email','username'];
$guarded = ['price'];

1
我正在寻找为我的列添加公共属性,以便我可以在编码时获得自动完成和拼写错误检测等好处。 - dotNET

0

这与PHP或Laravel无关,它是ORM的另一个“设计模式”:Active Record。Entity Framework是一个数据映射器ORM。有很多文章和资料解释了这两种模式,比如this one

其他几种语言和框架也使用Active Record ORM,比如Ruby on Rails和Python Django。

当然,这不是唯一的选择,PHP有Doctrine,一个像Entity Framework一样的数据映射器ORM。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接