When I run the following method, it returns a collection with soft deletes included...and obviously, it shouldn't.
return $twitter_oauth->get();
I think it might be the boot function in my TwitterOAuth model. I use the boot meth below to soft delete relevant models (works as it should).
public static function boot()
{
TwitterOAuth::deleting(function($twitter_oauth) {
$twitter_oauth->posts()->delete();
});
TwitterOAuth::restoring(function($twitter_oauth) {
$twitter_oauth->posts()->withTrashed()->restore();
});
}
Now if I remove the boot method and run the same get query, soft deletes do not appear in the collection. Weird. Anyone have an experience or run into this issue - or see my problem?
I know I could use whereNull in my queries, but that seems like a hack. There must be a better way...
Needed to include parent::boot(); in my boot method. Solved it.
public static function boot()
{
parent::boot();
TwitterOAuth::deleting(function($twitter_oauth) {
$twitter_oauth->posts()->delete();
});
TwitterOAuth::restoring(function($twitter_oauth) {
$twitter_oauth->posts()->withTrashed()->restore();
});
}
Related
I have been using Traits for some of the repetitive functionalities such as Generating guid or logging some events. But now I need to do something more robust such as logging all of the user activities.
Here is a sample of my code:
public static function bootLoggable()
{
static::creating(function ($model) {
dd($model);
});
}
Aside from the above one, I also would like to add all of the available model listeners, but for example, if I add the below one for instance
public static function bootLoggable()
{
static::creating(function ($model) {
dd($model);
});
static::updating(function ($model) {
dd($model);
});
}
it no longer seems to work, I tried to find some more information on the topic, but couldn't get too far. I'm clearly doing something against the working logic of these events.
It may be hard to explain this case via title so let me put it here.
I had to use Laravel Boilerplate. It uses UUIDs for models. It has a trait that when User model is being created, it adds UUID.
protected static function boot() // this is in the trait
{
parent::boot();
static::creating(function ($model) {
$model->{$model->getUuidName()} = PackageUuid::generate(4)->string;
});
}
My Eloquent User uses this trait. Everything was working but one day I had to add global scope to User so I defined in the User class:
protected static function boot()
{
parent::boot();
static::addGlobalScope(new UserScopeSalesContactAdmin());
}
I totally missed that. Now my global scope works but UUID is ignored. I obviously know why this happens. I'm not the author of the Boilerplate and I would architecture this in a different way but it's a nice educational case hence my question:
How can I change my code the way that both my global scope works as well as UUID? I could simply move my addGlobalScope to the trait but it's obviously stupid idea as scope has nothing to do with UUID hence it violates the OOP rule.
edit:
I did other way around: I copied:
static::creating(function ($model) {
$model->{$model->getUuidName()} = PackageUuid::generate(4)->string;
});
to the boot method in the User but still I consider this as a duplication…
You can write your own trait. But instead of boot() define boot<TraitName>():
trait HasUuid
{
protected static function bootHasUuid()
{
static::creating(function ($model) {
$model->{$model->getUuidName()} = PackageUuid::generate(4)->string;
});
}
}
This way you don't need to override boot().
Laravel will call the special boot method of traits based on the naming scheme described above. It's just not well documented functionality...
You can try this assuming the name of the trait is BoilerplateTrait. Call the BoilerplateTrait boot function from inside the boot function of you User model
protected static function boot()
{
//parent::boot();
BoilerplateTrait::boot();
static::addGlobalScope(new UserScopeSalesContactAdmin());
}
I have a boot() function in my model that looks like this,
public static function boot() {
parent::boot();
// new item created
static::created(function($proof) {
//Fire an event
}
}
the created function fires everytime I fire something like this, $proof->save()
is there a way to by pass firing the created function, there are some scenarios where I do not want to run the functionality within that method?
If it makes any difference I am running Laravel 4.2
Run flushEventListeners() before you do the create. Example:
$data = [];
if ( //SOMETHING ) {
User::flushEventListeners();
}
User:create($data);
Note that this will prevent all events from firing, not just created.
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 ?
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).
I am trying to prevent a model from saving to database, if it has certain value in a property.
Inside service provider:
public function register()
{
$this->registerSaveRelated();
$this->registerEvents();
}
public function registerEvents()
{
$app = $this->app;
$app['events']->listen('eloquent.saving*', function ($model) use ($app) {
$app['my_service']->checkModel($model);
});
}
public function registerSaveRelated(){
// More code...
checkModel($model) gets fired, as expected. However, nothing happens if I return false. The model continues to save. What is the correct way to do it?
It needs to be done in the package, not model. So Model::saving() is not an option.
There are two solutions that I can think of.
A. Use Model::saving() and just differentiate between the packages using a boolean or other simple logic.
B. Override the save method and put the logic in that function itself.
I think you are on the right track. You are correct that checkModel needs to return true or false. The important part, however, is what the closure passed to listen returns. That is what determines whether it saves or not. Right now its not returning anything. Try adding return like so:
public function registerEvents()
{
$app = $this->app;
$app['events']->listen('eloquent.saving*', function ($model) use ($app) {
return $app['my_service']->checkModel($model);
});
}