Skip to main content

Laravel8 in Serializes Models trait | laravelnote

This article was originally posted, with additional formatting, on my personal blog at laravel serializes model

Background 
When dispatching an object onto the queue, behind the scenes Laravel is recursively serializing the object and all of its properties into a string representation that is then written to the queue. There it awaits a queue worker to retrieve it from the queue and unserialize it back into a PHP object (Phew!).

Problem
When complicated objects are serialized, their string representations can be atrociously long, taking up unnecessary resources both on the queue and application servers.

Solution
Because of this, Laravel offers a trait called SerializesModels which, when added to an object, finds any properties of type Model or Eloquent\Collection during serialization and replaces them with a plain-old-PHP-object (POPO) known as a ModelIdentifier. These identifier objects represent the original properties Model type and ID, or IDs in the case of an Eloquent\Collection, with a much smaller string representation when serialized. When these objects are unserialized, the ModelIdentifiers are then replaced with the Model or Eloquent\Collection of Models that they temporarily represented.

ℹ️ Curious to know how the SerializesModels trait is "replacing" these properties at runtime? Before jumping into the source code, you may want to read the PHP docs for a quick primer on what the reflection API offers. For a more detailed explanation including examples of how Laravel uses reflection check out this article.

Gotcha!
🗣 Because of the SerializesModels trait that the job is using, Eloquent models and their loaded relationships will be gracefully serialized and unserialized when the job is processing.

While this quote from the docs sounds promising, it can be misleading. Here is an example of how a Model would be represented when serialized.

https://i.imgur.com/KwnEZsN.png

As you can see, the relation is serialized as records, which is the name of a one-to-many relation between User and Record as it exists on the User model. However, even though we're only selecting the id and user_id columns and have a limit of 3 records to be returned with the User model, there's no mention of which records, which properties, or how many in the serialized representation.

Let's see what the query log looks like when we unserialize this object.

https://i.imgur.com/1oMrVXI.png

Wow! We're selecting all Record models, in their entirety, associated to the User! As you can probably imagine, this can cause unforeseen issues for the unexpecting artisan.

Workarounds
Hope is not lost! There are workarounds without having to sacrifice resources on the queue, application, or database servers.

Unload unnecessary relations
If you don't need the loaded relations to be re-loaded, you can simply call withoutRelations() on your Model before it's serialized.

https://i.imgur.com/UZ4j14E.png

As you can see, there are no longer any relations that will be loaded when the User model is unserialized.

Make necessary relations their own property
If the loaded relation is going to be required after the Model is unserialized you can store the relation (which is an Eloquent\Collection) as its own property on the object being serialized.

https://i.imgur.com/VnJ1lKY.png

As you can see, the serialized representation of the Eloquent\Collection specifies what the Model type is and which IDs need to be retrieved from the database.

https://i.imgur.com/WdPdrMl.png

Much better! Now that our Model and Eloquent\Collection of Records have been hydrated, we can even set the relation back on the Model by using the setRelation method.

Recap
While it's recommended to use the SerializesModels trait on any and all objects that will be queued (or otherwise serialized) it is crucial to be at least aware of its potential pitfalls and shortcomings as well as how to avoid running into them, if not understand how it all works under the hood.

🎉 Like the content? Feel free to let me know on Twitter where I share all of my articles, packages, and occasional opinions (mostly technical).

Popular posts from this blog

Laravel Parallel Testing Is Now Available in laravel8 | Laravelnote

 Parallel Testing | Laravelnote As such we know Laravel and PHP Unit execute your tests sequentially within a single process.  As such laravel check the single process doesn’t use multiple cores so that therefore, your test execution is seriously bottlenecked! we glad to say that Parallel Testing is now available in Laravel. You can use this Laravel version8.25 you may also use to laravel8 built-in test Artisan command to run your cmd to tests simultaneously across multiple processes to use significantly reduce the time required for to run the entire test suite. It is about sure that in laravel8 new on top of Paratest Laravel automatically use to handles creating and migrating a test for database for each parallel process. In The  Laravel8 for testing purpose goodies - such as Storage::fake - are ready for used in Parallel too. Laravel Provide Each all individual laravel8 version use test suite will receive a varying benefits from parallel testing. In The Laravel Tests are execution wa

What is HTTP client in laravel8 by laravenote 2021 | Laravelnote

Laravel provides an expressive, minimal API around the Guzzle HTTP client, allowing you to quickly make outgoing HTTP requests to communicate with other web applications. Laravel's wrapper around Guzzle is focused on its most common use cases and a wonderful developer experience. Before getting started, you should ensure that you have installed the Guzzle package as a dependency of your application. By default, Laravel automatically includes this dependency. However, if you have previously removed the package, you may install it again via Composer: composer require guzzlehttp/guzzle Making Requests To make requests, you may use the get, post, put, patch, and delete methods provided by the Http facade. First, let's examine how to make a basic GET request to another URL: use Illuminate\Support\Facades\Http; $response = Http::get('http://example.com'); The get method returns an instance of Illuminate\Http\Client\Response, which provides a variety of methods that may be use