Laravel Cheatsheet - Models, Factories, and Database Queries
Related Posts
Table Of Contents
Models
Artisan - Create Model Class
php artisan make:model MyModel
Manually Specifying Table Name
If Laravel can't correctly guess your table name from your model's name, then you may need to
manually specify it by setting the $table
attribute.
protected $table = 'my_table_name';
Using UUID Primary Keys
I created a separate post for creating a trait that one adds to a model for making it use UUIDs for the primary key/identifier, but is you wish to manually add the code to the model, then you would need to perform the steps below:
If you are using UUIDs as your primary key, be sure to add the following to your models:
protected $primaryKey = 'uuid';
Also, be sure to add the uuid
to your model's $casts
attribute so it remains a string rather
than trying to be casted to an integer.
protected $casts = [
'uuid' => 'string',
];
Also be sure to set (otherwise it will be cast to integer after calling save()
):
public $incrementing = false;
Soft Deletes
To make use of Laravel's soft-delete functionality, just make sure to add the
\Illuminate\Database\Eloquent\SoftDeletes
trait like so:
class User extends Authenticatable
{
use \Illuminate\Database\Eloquent\SoftDeletes;
Then make sure to have the deleted_at
column in the relevant table, allowing null values. If you
set Laravel to using unix timestamps (below), then it needs to be an integer type.
Unix Timestamps for created_at and updated_at
If you would rather not use the SQL timestamp type, and prefer to work with unix timestamp integers, you can get Laravel to do this by editing your model and adding:
public function getDateFormat() { return 'U'; }
Disabling Timestamps
By default, Laravel models are expecting the tables to have the created_at
and updated_at
columns. If you don't want to have these, then add the following to your model:
public $timestamps = false;
Seeding
Artisan - Create Seeder Class
php artisan make:seeder MyTableSeeder
If this seeder is used for populating the database, then you may wish to call it from the
DatabaseSeeder.php
file like so:
public function run()
{
$this->call([
MyTableSeeder::class,
]);
}
Factories
Whilst creating your seeder, you are probably going to want to create a lot of model objects with random data, to populate your database to test/dev with. This is where factories come in. Whilst factories could be used for creating "real" models, it is best to use them for creating "fake" models for seeding with. This is why mass assignment protection is automatically disabled when creating models through factories..
Create Factory
Use the following command to create a factory for "MyModel".
php artisan make:factory MyModelFactory --model=MyModel
--model=MyModel
part.
Apply Factory To the Model
Once you have created the factory, you need to "connect" it to the model (e.g. the model will use that factory). This is because in Laravel, one performs operations on the model, rather than calling the factory directly.
We do this by adding the Illuminate\Database\Eloquent\Factories\HasFactory
trait to the model.
Laravel will use its smarts to try and auto-magically figure out the factory that relates to the model, but one can manually specify the factory by overwriting the newFactory method like so:
protected static function newFactory()
{
return Database\Factories\Administration\MyFactory::new();
}
Using The Factory
Once you have created the factory, you can call it to create a bunch of your models:
$numberOfModelsToCreate = 10;
$generatedModels = MyModel::factory()->count($numberOfModelsToCreate)->make();
This will generate the models with the default values specified by the factory.
If you wish to specify certain attribute values for the generated models, then provide them in an associative array like so:
$generatedModel = MyModel::factory()->make([
'age' => 18,
]);
If you wish for your factory to create models with a varying set of values, then you can use a sequence like so.
$sequence = new Sequence(
['sex' => 'M'],
['sex' => 'F'],
);
$generatedModels = MyModel::factory()
->count(10)
->state($sequence)
->create();
Alternatively, one could specify a closure for the sequence to use based on the index. More info about that in the official docs.
Using the Factory - Creating The Related Objects
In the previous section, we created one set of objects using the factory. It won't take long before you need to create a set of models, with the other models that relate to it. For example, one may wish to create a set of users, with each user having a set of blog posts that they created. If we have set up the has many relationship, and the factories for both models, then this is as easy as:
$user = User::factory()
->has(BlogPost::factory()->count(3))
->create();
Obviously, its a bit strange if all of our users have exactly 3 blog posts. It would be good if we can have a random count for the number of sub-objects, but I am not sure if that is possible at this point in time. I will update this post if I figure it out or if someone kindly provides a solution in the comments.
Additional Factory Features
This is just a cheatsheet to cover the basics. You may be interested in some of these topics though:
Database Queries
Where Clauses
The following queries all do the same thing:
User::where('email', $email)->exists();
User::where('email', '=', $email)->exists();
When you need to perform multiple where clauses:
$exists =
MyModel::where('user_id', '=', $userId)
->where('course_id', '=', $courseId)
->exists();
// My preferred way
$whereClauses = [
['user_id', '=', $userId],
['course_id', '=', $courseId],
];
$exists = MyModel::where($whereClauses)->exists();
The above examples are all for AND
clauses. If you need an OR
relationship then you need to use
orWhere
. E.g.
$exists =
MyModel::where('user_id', '=', $userId)
->orWhere('course_id', '=', $courseId)
->exists();
Example Update Statement
The following query will update the users table to set the ID to a new UUID where the legacy_id matches $legacyId
DB::table("users")
->update(["id" => Str::orderedUuid()])
->where("legacy_id", "=", $legacyId);
Batch Update
It can seem that performing a batch update is impossible, or extremely difficult, but actually it's easy enough, but one needs to use escaping(through PDO quote) instead of prepared statements. It seems that Laravel is completely built around the use of prepared statements instead.
Here is an example of how one can do a batch update, whereby I am generating new UUID values for each row in the users table.
$users = DB::table("users")->select("legacy_id")->get();
$updateQueries = [];
foreach ($users as $user)
{
$escapedUuid = DB::getPdo()->quote(Str::orderedUuid());
$updateQueries[] = 'UPDATE "users" SET "id" = ' . $escapedUuid . ' WHERE "legacy_id" = ' . $user->legacy_id;
}
// one has to use "uprepared" because all other operations use prepared statements that can't handle multiple
// queries in one go.
$bulkUpdateQuery = implode(";", $updateQueries) . ";";
$result = DB::unprepared($bulkUpdateQuery);
Checking Uniqueness
Often you want to check if a record already exists before attempting an insert. For example, when adding a new user, you wish to check that a user with the same email doesn't already exist. For this, do the following:
if (User::where('email', '=', $email)->exists())
{
throw new Exception("A user with that email already exists.");
}
Loading By Unique Value
If you have a unique attribute on your table and you want to load based on that, you can put something like this in your model:
/**
* Fetch an account from the email address
* @param string $email - the email of the account we wish to fetch.
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
*/
public static function loadFromEmail(string $email) : Account
{
return Account::where('email', '=', $email)->firstOrFail();
}
The beauty of this is that you no longer need to use type hinting comments as you have a return type on your function.
Pluck
You can use ->pluck()
to just get the values of one column. For example, the code below will get
you a collection object of all the titles of posts where the category_id
is 3
.
$postTitles = DB::table("posts")
->where("category_id", 3)
->pluck("title");
Illuminate\Support\Collection
Converting Collection To Array
Queries will return Laravel collections. However, you often want the array form instead. You can do
this by executing the ->all()
method on the collection.
The example below will get an array of all the post titles.
$postTitles = DB::table("posts")
->where("category_id", 3)
->pluck("title")
->all();
Raw Select
Sometimes you need to do a complicated select query and it may be easier just to write it raw. Here is an example:
$rawSelectQuery =
"SELECT `id` FROM `myTable`" .
" WHERE `type_id`={$myTypeObject->id}" .
" AND `id` in (" .
"SELECT `some_id` FROM `table2` WHERE `someColumnName`={$someValue}" .
")" .
" OR `id` in (SELECT `some_id` FROM `global_items`)";
$results = \DB::select($rawSelectQuery);
Inserting Data
This is one way you could insert multiple rows into a database at once.
DB::table('users')->insert([
['email' => 'taylor@example.com', 'votes' => 0],
['email' => 'dayle@example.com', 'votes' => 0]
]);
Migrations
Change Column Type
Schema::table('my_table_name', function (Blueprint $table) {
$table->bigInteger('my_column_name')->change();
});
Rename Column
Schema::table('my_table_name', function (Blueprint $table) {
$table->renameColumn('original_name', 'new_name');
});
Drop Table
Schema::drop('my_table_name');
Rename Table
Schema::rename('original_table_name', 'new_table_name');
References
- Stack Overflow - Disable Laravel's Eloquent Timestamps
- Stack Overflow - Laravel timestamp saving time as unix timestamp
- Stack Overflow - Laravel Checking If a Record Exists
- Stack Overflow - Why does Eloquent (in Laravel) cast my varchar uuid primary key to an integer resulting in errors?
- Laravel-news.com - Laravel firstOrFail forTheWin
- Stack Overflow - Laravel UUID generation
- Stack Overflow - Bulk Insertion in Laravel using eloquent ORM
- Stack Overflow - laravel select where and where condition
- Stack Overflow - How to Create Multiple Where Clause Query Using Laravel Eloquent?
- Stack Overflow - Can you create a new Model instance without saving it to the database
- Stack Overflow - Laravel migrations change a column type from varchar to longText
First published: 18th September 2021