【问题标题】:Laravel models: Where are model properties?Laravel 模型:模型属性在哪里?
【发布时间】:2019-01-15 15:45:41
【问题描述】:

(我来自 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. 是否有工具/扩展可以检查我的数据库并创建具有列属性的模型?

更新

感谢回答我问题的人,非常感谢。另外,我发布的一些 cmets 是由于我对 PHP(奇怪的 IMO)关于会员访问的方法一无所知;我刚刚发现 PHP 不会抱怨不存在的类成员,而是动态生成它们(例如 $post->NonExistingMember = SomeValue 运行正常;这甚至不会在我知道的大多数其他语言中编译)。给我很大的惊喜。我在其他几种语言中使用过 C++、VB、C#、Java,并且在其他任何地方都没有看到过这种行为。所有这些语言都会立即抛出编译时错误,说诸如 Type X 不包含名为 Y 的成员。看不到 PHP 的不同方法如何与 OOP 结合在一起。

我发布此问题的实际问题仍未解决。虽然我可以使用reliese/laravel 为我的数据库生成模型类,但该工具仍然不会针对表列生成类成员,因此我没有获得自动完成的好处。如果可以(当然是自动完成),我很乐意听取专家的意见。

更新 2

现在我对 Laravel 环境有了更好的了解,我想我会分享我的经验。请参阅下面的答案。

【问题讨论】:

  • 也许这可以帮助你:*.com/questions/30560485/…
  • 也许这会有所帮助:Laravel: Eloquent Getting Started。但基本上,是的,你是手写的。有一些可用的包可以从现有数据库生成模型
  • @RafaelBoszko:这击中了头上的钉子(钉子的头是:))。谢谢。
  • 您是否曾经解决过此问题以便获得自动完成的好处?我也来自 C#,并且习惯于更明确而不是“自动”的东西。
  • @LouiseEggleton:我花了更多时间在 Laravel 上工作。基本问题(或称其为特性)是类不需要在编译时包含成员,因为可以说没有编译时。 PHP 在运行时解释代码并动态生成成员。所以模型类根本不需要你有任何成员。这与 C# 世界中的强类型模型(实体甚至数据集)非常不同,在类属性和底层表列之间具有 1-1 对应关系。所有这意味着 VS Code 中不会有自动完成的东西Laravel。

标签: php laravel orm model eloquent


【解决方案1】:

现在我已经花了一些时间在 Laravel、Eloquent 和 PHP 上,我将分享一些东西,希望这些对其他初学者有所帮助。

PHP 是一种动态语言,它的代码是动态编译的(这与 C# 和 VB.NET 不同)。您的模型类不需要显式定义成员以使其可访问/可分配,因此只要它们扩展 Model(Laravel Eloquent 中的内置类),您就可以将值分配给具有相同的不存在成员name 作为基础数据库表列,Eloquent 会为您将其存储在数据库中。例如,如果您的数据库中有一个posts 表,其中有一个名为body 的列,您可以编写以下代码在数据库中创建一条新记录:

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

当然,您的项目中需要有一个从Model 扩展的类Post,但您不需要在该类中定义成员body。对于来自 .NET 世界的人来说,这听起来很奇怪,但动态语言就是这样工作的。

至于自动生成模型,Laravel 包含内置命令 (php artisan make:model) 可以为您生成模型。

最后,对于智能感知和自动完成,请使用 Laravel 本身使用的相同工具,即 DocBlocks。这些是 PHP 中特殊类型的 cmets,您可以使用它们记录您的代码元素。因此,您可以将 DocBlocks 添加到所有包含属性名称和类型的模型类中。对每个人来说幸运的是,VS Code 中有一个非常简洁的扩展,可以自动为您完成这项工作。使用以下命令安装它:

composer require --dev barryvdh/laravel-ide-helper

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

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 成员):

请注意,我的 VS Code 中也安装了 Intelephense,但我不确定自动完成功能是否需要这样做。

【讨论】:

  • 您不需要定义属性,但实际上您不得定义属性。定义它们打破了 Eloquent 的假设。这是一个我不理解的设计选择,但更烦人的是 Laravel 文档页面上没有提到。
  • 当我从 symfony 迁移到 laravel 时,这让我很困惑。像你自己一样,我编写了许多语言,我更喜欢严格类型,你可以在 7+ 中编写严格或宽松的 php。在 symfony 中,我对数据库使用理论 orm,它们被称为实体而不是模型,但它们只是表的编码版本,并使用存储库进行查询。实际上,您使用 getter 和 setter 声明属性。 Laravel 使用魔法方法来创建属性,我不喜欢它,但我确实喜欢 laravel,所以我接受它。使用 docblock 方法可以很好地平衡它
【解决方案2】:

我使用注释来声明自动完成的所有属性(在 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 array)
    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',
    ];
    

    您还可以使用Accessors 在模型中注入新属性

    1. 我不这么认为,你可以安装一些 Laravel VS Code 插件来让你的生活更轻松(例如:Blade sn-ps) 您可以查看this article

    【讨论】:

    • 谢谢。您可以在答案中包含您的模型类定义吗?
    • 检查我更新的答案,我希望这是你所要求的。如果您需要其他内容,我可以更新我的答案。
    • 现在你已经接近我的要求了。上面代码中的$newPost->title 是什么?您的班级中没有这样的属性或公共变量。即使有,该变量如何映射到基础表列?
    • 标题是数据库中的“标题”列,如果您需要创建帖子并添加“标题”值,则需要在“$fillable”数组中添加标题。
    • 默认情况下你可能还有一个由 Laravel 创建的“id”、“created_at”和“updated_at”,但是在这个数组中你只需要声明你需要更改的属性。
    【解决方案4】:

    其实你不需要指定属性。这一切都是由 laravel 自动完成的。当你创建一个模型时,laravel 使用类名的复数形式(它有一个非常好的系统:post 变成 post,activity 变成 activity 等等)来访问表。因此,您可以在不设置 $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 类的成员。
    • 原谅我的无知。我刚刚发现 PHP 不会抱怨不存在的类成员,而是动态生成它们。给我很大的惊喜。我在其他几种语言中使用过 C++、VB、C#、Java,并且在其他任何地方都没有看到过这种行为。所有这些语言都会立即抛出一个编译时错误,比如Type X does not contain a member named Y。看不到 PHP 的不同方法如何与 OOP 结合在一起。
    【解决方案5】:

    您需要自己添加。例如

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

    【讨论】:

    • 我正在寻找针对我的列添加公共属性,以便我获得代码时间优势,例如自动完成和拼写错误检测等。