Initial Data in Laravel Projects

Initial data for a project, such as roles (e.g., super admin role), permissions, and which users are granted admin privileges, is typically designed during the requirements analysis phase.

This initial data is a part of the project’s operation and will be used in the production environment. However, data seeding is generally used during the development phase.

Although Laravel doesn’t offer a built-in solution for this, we can leverage the database migration feature to achieve it. In terms of functionality, data migration is also part of the project, with the execution timing aligning perfectly with the installation of the project. The execution order is critical, ensuring that the initialization data is applied after the database table structure is created.

We can generate migration files for initializing data using the following command:

php artisan make:migration seed_categories_data

We define the naming convention for such migration files as seed_(table_name)_data.

For project initialization data, using a Seeder is less convenient than using a database migration file, especially in collaborative development. With multiple migration files from different developers, you only need to execute a single migration command. If issues arise, you can easily roll back, whereas Seeders lack rollback functionality. Rollback is crucial, both in development and production environments. If the initial data can be fully determined before the project is deployed (which is almost impossible), then using a Seeder is acceptable and recommended. However, after the project goes live, it’s likely that the initial data will require modifications (such as additions, deletions, or updates). In such cases, using a Seeder is inappropriate, and database migration files should be used instead. Seeders are primarily used for generating test data, and they should not be used for altering or deleting production data.

Laravel项目的初始化数据

项目的初始化数据,例如一个应用程序有哪些角色(例如超级管理员角色)、哪些权限、授予哪些用户管理员权限是在需求分析阶段就设计好的。

项目的初始化数据是项目运行的一部分,在生产环境下也会使用到,而数据填充(Seeder)一般在开发时使用。

虽然Laravel没有自带此类解决方案,不过我们可以借助数据迁移功能来实现。在功能定位上,数据迁移也是项目的一部分,执行的时机刚好是在项目安装时。并且区分执行先后顺序,这确保了初始化数据发生在数据表结构创建完成后。

我们可以使用命令生成数据迁移文件,作为初始化数据的迁移文件:

php artisan make:migration seed_categories_data

我们定义这种迁移文件的命名规范为seed_(数据库表名称)_data

对于项目的初始化数据,使用Seeder没有使用数据库迁移文件来得方便,特别是在多人协作开发时,对于别的开发者提供的多个数据库迁移文件,你只需执行一条迁移命令即可,如果有问题还能回滚,Seeder没有回滚功能。回滚功能无论是对于开发环境还是生产环境都是很重要的。 如果项目的初始化数据在项目上线(部署到生产环境)之前,就能全部确定下来(这几乎不可能),那么使用Seeder是可以的,也是应该的。但是在项目上线之后,很可能还要对初始化数据进行增删改操作,此时再使用Seeder是不合适的,应该使用数据库迁移文件。Seeder主要用来生成测试数据,很难、也不要用Seeder来删改数据库数据。

Laravel’s DB Facade Doesn’t Trigger Eloquent ORM Model Events

When using the DB facade to perform database operations in Laravel, Eloquent ORM model events like saving, saved, etc., are not triggered.

If you want to avoid triggering model events in event listeners or queued tasks, aside from using Eloquent’s saveQuietly, deleteQuietly, and similar methods, you can directly use the DB facade to execute database operations.

This approach allows you to bypass Eloquent’s event handling when necessary.

Laravel使用DB façade执行数据库操作不会触发Eloquent ORM模型事件

使用DB façade执行数据库操作不会触发saving、saved等Eloquent ORM模型事件。当我们不想在事件监听器或队列任务中触发Eloquent ORM模型事件时,除了使用Eloquent ORM模型的saveQuietly、deleteQuietly等方法之外,还可以直接使用DB façade执行数据库操作。

在Eloquent ORM模型事件监听器和队列任务中,要避免使用Eloquent模型增删改查方法,例如create、update、save等。否则会陷入调用死循环 —— 模型事件监听器分发队列任务,队列任务触发模型事件,模型事件监听器再次分发队列任务,队列任务再次触发模型事件……死循环了。

Modify and Persist Model Instances in Laravel Using the saved Event, Not the saving Event

In Laravel, don’t call the save method on a model instance inside the saving event listener.

If you need to modify a model’s field and persist it within a model event listener, make sure you’re using the saved event, not the saving event. This is particularly important when your event listeners are queued for asynchronous execution.

The saving event occurs before the model is persisted to the database. If you try to modify a field and call save() within this listener, it won’t actually persist to the database, especially when the listener is queued for async execution. For example, modifying the slug field might not actually update in the database.

Instead, use the saved event listener and call saveQuietly to persist the changes, as shown in the example:

static::saved(queueable(function (Topic $topic) {
    // If the slug is empty, translate the title into a slug
    if (!$topic->slug) {
        $topic->slug = app(SlugTranslateHandler::class)->translate($topic->title);
        $topic->saveQuietly();
    }
}));

By using the saved event and saveQuietly, you ensure that your changes are made after the model is successfully persisted, avoiding any issues with asynchronous queue execution.

Laravel框架应该在saved而不是saving事件监听器中修改模型实例并持久化

不要在saving事件的监听器中运行模型实例的save方法。

要在模型事件的闭包监听器中修改模型实例的某个字段的值并持久化,当模型事件的闭包监听器作为队列任务异步执行时,不能监听saving事件,因为该事件表示模型实例还未持久化到数据库里,因此监听器作为队列任务异步执行的话,就会导致模型实例的某个字段(例如下例的slug字段)的值不能真正持久化到数据库里(记住,不应该在saving事件的监听器里调用模型实例的save方法)。应该在saved事件监听器里saveQuietly模型实例,例如:

static::saved(queueable(function (Topic $topic) {
    // 如果slug字段无内容,就使用翻译器对title字段进行翻译
    if (!$topic->slug) {
        $topic->slug = app(SlugTranslateHandler::class)->translate($topic->title);
        $topic->saveQuietly();
    }
}));

When Clients Don’t Need to Initialize CSRF Tokens in Laravel

In Laravel, clients don’t need to initialize CSRF tokens under the following conditions:

  • Cookie and Session-based Authentication: When using cookie and session-based user authentication, and the route being accessed is part of web.php with the App\Http\Middleware\VerifyCsrfToken middleware enabled, CSRF tokens are required.

Authorization Points in the Laravel Framework (Where Authorization Takes Place)

In the Laravel framework, authorization can be implemented in the following places:

  • Using the can Middleware: This middleware allows for permission checks at the route level, providing an easy way to ensure that the user has the required authorization.
  • Using the authorize Method in Form Request Validation Classes: The authorize method is used to determine whether the user is authorized to make a given request. Note that if you generate a form request validation class using the php artisan command, it will come with a default return false in the authorize method.
  • Using authorize, can, or cannot Methods in Controller Actions: Within controller methods, you can use these methods to check if the user has the required permissions before performing an action.
  • Using @can and @cannot Directives in Blade Templates: These Blade directives allow you to conditionally display content based on whether the user has a specific ability or permission.
  • Using Sanctum Token Abilities: When using Sanctum for API authentication, you can define and check token abilities to manage access at a granular level.

Laravel框架的鉴权点(在框架的哪些地方鉴权)

可以在Laravel框架的以下地方鉴权:

  • 使用can中间件
  • 使用表单请求数据验证类的authorize方法。注意,使用php artisan命令创建的表单请求数据验证类,默认包含return false的authorize方法
  • 在控制器的方法里使用authorize、can、cannot等方法
  • 在Blade模板中使用@can、@cannot等指令
  • 使用Sanctum的令牌能力