Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Eager loading has some unexpected side effects on Model events/ booting in Laravel

I'm trying to create some tests.

Here's my Test Class:

class ExampleTest extends TestCase {

    public function setUp()
    {
        parent::setUp();
        Artisan::call('migrate');
        $this->seed();
        Auth::loginUsingId(1);
    }
    public function testActionUpdateNew()
    {
        $action = new Action(Array());
        $action->save();
        var_dump($action->id);
        Action::with('reponses','contact','user','etudiant','entreprise','etude')->findOrFail($action->id);
    }
    public function testEtudes()
    {
        $etudes=Etude::all()->toArray();
        $this->assertCount(10, $etudes, "Nombre d'études incorrectes");
        $numEtudes=count($etudes);
        //Buggy part
        $etude= Etude::create(Array());
        var_dump($etude->id);
        $etudes=Etude::all()->toArray();
        $this->assertCount(11, $etudes, "Nombre d'études incorrectes");
        //10+1 should equal to 11 but it hasnt updated
    }
}

The test that is not passing is the second one: I count the number of eloquent Objects Etudes, which are of 10 at the beginning, I then add one etude to the database (using Etude::create()) , the object is created, because $etude->id gives out a real number. Howewer, the number of Etude hasn't updated.

The problem does go away when I remove the 'etude' from the eager loading in Action::with('reponses',...)

Here is the etudes relationship in the Action class:

public function etude() {
    return $this->belongsTo('Etude');
}

Do you guys have any idea if eager-loading in laravel can have such strange behavior and how to fix that ?

EDIT

I found out that calling with('etude') had the action to remove the events registered to the Eloquent Model:

boot Method of Etude:

public static function boot()
{
    parent::boot();

    static::creating(function($etude)
        {
                       var_dump("creating etude"); //This doesn't get executed even when I run Etude::create(Array());
        }
    );
}

So If I add Etude::boot() at the beginning of testEtudes, it works again. This is still strange.

Does eager loading has any effect on events or the boot method ? Or is the boot method not called automatically after each test ?

like image 756
edi9999 Avatar asked Nov 22 '13 17:11

edi9999


People also ask

What is the benefit of eager loading when do you use it Laravel?

Eager Loading: Eager Loading helps you to load all your needed entities at once. i.e. related objects (child objects) are loaded automatically with its parent object. When to use: Use Eager Loading when the relations are not too much. Thus, Eager Loading is a good practice to reduce further queries on the Server.

How eager loading works in Laravel?

Eager loading is super simple using Laravel and basically prevents you from encountering the N+1 problem with your data. This problem is caused by making N+1 queries to the database, where N is the number of items being fetched from the database.

What is Eloquent in Laravel?

Eloquent is an object relational mapper (ORM) that is included by default within the Laravel framework. An ORM is software that facilitates handling database records by representing data as objects, working as a layer of abstraction on top of the database engine used to store an application's data.


1 Answers

In Laravel tests, the event dispatcher is reset between each test, but the models are still only booted once as they live a pretty independent life. This means that between each test, the model listeners are erased but never re-registered. The solution is to not use boot() for registering model events, but rather but them in a separate file - either a service provider or a file included from app/start/global.php (app/events.php is a common one).

like image 159
Andreas Avatar answered Sep 27 '22 23:09

Andreas