I use laravel 5.8 for my application. I have variables call "lot" and when I delete one, I want to perform other actions.
So I use deleting function on my model, everything works OK.
But know I have a function on my controller to delete many "lots" and I want actions to be perform only when all "lots" have been delete and not on every "lots" delete.
So I wonder if there is a way to achieve this ? Maybe we can pass a variable to boot functions to trigger or not the function ?
My model looks like this :
protected static function boot()
{
parent::boot();
static::deleted(function($modele) {
Etage::doesntHave('lots')->delete();
}
}
Model events are been designed to work for single models.
You should use Laravel Events instead, not tied to Models.
In EventServiceProvider.php register a new Event/Listener in the $listens array, like
LotsDeleted::class => PerformOtherAction::class
Create those classes with php artisan event:generate
Then, when you have finished deleting all your Lot objects, trigger the event with
event(new LotsDeleted());
The handle() function of your listener will be called and you can perform other actions.
Related
I am using Laravel Job Batching feature and I have dashboard where I display the progress of Batch(Processed, Failed, Pending jobs … etc.).
Each user has it's own dashboard and I want to display the progress of Batch based on logged in user, but I can't see any relationship with User model with batch table job_batches.
Is it possible to somehow make relationship with those tables? or any alternative?
Thanks
That is possible, but there is a lot of hoops to go through. This could also be a question about, a general approach to extending functionality of Laravel.
Some quick assumption is that you use some sort of Authentication when creating the batches, so you can do Auth::user()->id.
Create your user_id for the job_batches table with a migration.
Schema::table('job_batches', function (Blueprint $table) {
$table->unsignedBigInteger('user_id')->after('name')->nullable();
$table->foreign('user_id')->references('id')->on('users');
});
Laravel uses a BatchRepository to create the Batches in the job_batches table, extend this and add our logic to insert Users into the row. I have added the custom repository, to App\Repositories namespace. In general use the current logic and update the user_id after the core Laravel logic has been executed.
<?php
namespace App\Repostories;
use Illuminate\Bus\DatabaseBatchRepository;
use Illuminate\Bus\PendingBatch;
use Illuminate\Support\Facades\Auth;
class BatchRepository extends DatabaseBatchRepository
{
public function store(PendingBatch $batch)
{
$batch = parent::store($batch); // TODO: Change the autogenerated stub
$this->connection->table($this->table)
->where('id', $batch->id)->update([
'user_id' => Auth::user()->id,
]);
return $batch;
}
}
For Laravel to use your new class, you need to extend the current class in the Container. Third parameter is the table name, assuming you are using the default table. This is done in a provider. Either put it in existing provider, or create a new one, remembers to register it.
use Illuminate\Bus\BatchFactory;
use Illuminate\Bus\BatchRepository;
use Illuminate\Database\Connection;
use App\Repostories\BatchRepository as CustomBatchRepository;
...
public function register()
{
$this->app->extend(BatchRepository::class, function () {
return new CustomBatchRepository(resolve(BatchFactory::class), resolve(Connection::class), 'job_batches');
});
}
Tested with the following snippet, this will add user_id to the table rows.
Bus::batch([new TestJob(), new TestJob()])->dispatch();
The relationship
BatchRepositories returns a Batch that is not an Eloquent Model. So i would suggest creating your own Eloquent model for relationship purposes and make logic to convert it into the Batch when you want to have the batch functionality at hand eg. finished().
Firstly Eloquent Model for your Batch.php. Meanwhile also preparing the toBatch() functionality, to convert Eloquent model to Batch class.
namespace App;
use Illuminate\Bus\BatchRepository;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Batch extends Model
{
use HasFactory;
protected $table = 'job_batches';
public function toBatch()
{
return resolve(BatchRepository::class)->toBatch($this);
}
}
Create your relationship method on your User.php.
public function batches()
{
return $this->hasMany(Batch::class);
}
I tested the relationship setup with the following snippet, which worked.
User::first()->batches->first()->toBatch();
Secondly imagine having multiple batches, you would be able to get the Batch classes with higher order functions easily. Or else use them as a proper relationship.
User::first()->batches->map->toBatch();
Note
Be careful to import correct Batch and BatchRepository classes. I added imports to secure you include the correct ones, also the following snippet to the provider, makes you able to instantiate my custom batch repository.
use App\Repostories\BatchRepository as CustomBatchRepository;
$this->app->bind(CustomBatchRepository::class, function () {
return new CustomBatchRepository(resolve(BatchFactory::class), resolve(Connection::class), 'job_batches');
});
At your own risk, you can see my solution, in a rough testing ground created for this question. There is a controller and relationship on the user. Not certain if there is leftovers for other StackoverFlow projects.
Only I modify this for when is a job executed locally:
$this->connection->table($this->table)
->where('id', $batch->id)->update([
'user_id' => Auth::user()->id ?? null,
]);
I have a model that needs to delete images when the model is deleted. The model I will be calling is Seminar.php. This "Seminar" model has got instructors in the model called SeminarInstructor.php. Suppose I called Seminar::find(1)->delete(); to delete the seminar with id = 1-- I would also need this delete off any instructors connected to the model as below:
public function seminarInstructor()
{
return $this->hasMany(SeminarInstructor::class, 'seminar_id', 'id');
}
I have enabled cascading, so when I call delete, laravel/mysql should automatically resolve it. However, I have a method called public function deleteCloudImage1() in SeminarInstructor.php which handles the deletion logic for the image existing in my table as a url. I could manually call this method before I delete a seminar model, but I want to know if there is any convenient method to handle this in eloquent/laravel. I looked up the code a bit and there seems to be a callable attribute called onDelete in the implementation. Anyone know how to use this, or any other alternative.
I have override boot function inside the laravel model, code structure something like this:
class modelName extends Model
{
protected static function boot(){
parent::boot();
self::creating(function ($model){
//Do Some Stuff
});
}
This is working fine when I'm calling create a function, like this:
modelName::create($tmpArray);
But it's not working when I want to use insert function:
modelName::insert($tmpArray);
Now I want to call boot function when insert function called, I've two-dimensional array; in that case, I've only insert function to save data in a single row.
Events are only called when using the Eloquent functions to save/update/delete records.
If you want events to be triggered when saving multiple records, you could try to use Model::createMany($arrayOfObjects). However, checking the source code for this function, it will actually run a separate query for each record that's in the array. So if you got a whole lot, you might need to think of a different route.
creating is event of eloquent model.
insert() is not method of eloquent model, its method of db queries.
try to use eloquent method instead of db query.
see laravel doc for eloquent methods
see Db queries method
for two two-dimensional array can use eloquent method of createMany or saveMany
difference between save and create is that save accepts a full Eloquent model instance while create accepts a plain PHP array.
you can refer laravel docs Inserting & Updating Related Models
Laravel 5.8 is supposed to dispatch the syncing, attaching and detaching events (https://laravel.com/docs/5.8/releases search for Intermediate Table / Pivot Model Events section).
UPDATE: the release notes have been update after posting this question (more info: https://github.com/laravel/framework/issues/28050 - https://github.com/laravel/docs/pull/5096).
I tried it out but the following code throws the exception:
Call to undefined method App\ProjectUser::syncing()
NOTE: since Laravel 5.8 is supposed to dispatch the syncing event I don't want to use an external package.
class User extends Model
{
public function projects()
{
return $this->belongsToMany(\App\Project::class)->using(\App\ProjectUser::class);
}
}
class Project extends Model
{
public function users()
{
return $this->belongsToMany(\App\User::class)->using(\App\ProjectUser::class);
}
}
class ProjectUser extends Pivot
{
public static function boot()
{
parent::boot();
static::syncing(function ($item) {
dd('syncing event has been fired!');
});
}
}
// web.php
$project = \App\Project::first();
$project->users()->sync([1,2]);
I tried to move the boot method from ProjectUser to User and Project but I get the same exception.
On Laravel 5.8, when you are using the methods sync, attach or detach is going to be fired the appropriate model events (creating, updating, saving, ...) for the called action. Note that using sync, attach or detach is not going to fire any event like syncing, attaching or detaching.
More specifically, the sequence of events fired for each element passed to the sync method are:
saving
creating
created
saved
The sequence of events fired for each element passed to the attach method are:
saving
creating
created
saved
The sequence of events fired for each element passed to the detach method are:
deleting
deleted
So if you want to observe the syncing operation you actually have to observe the saving (or saved) event from the pivot model (in this case ProjectUser):
class ProjectUser extends Pivot
{
public static function boot()
{
parent::boot();
static::saving(function ($item) {
// this will die and dump on the first element passed to ->sync()
dd($item);
});
}
}
A working example https://github.com/danielefavi/laravel-issue-example
More info on this issue https://github.com/laravel/framework/issues/28050
The release notes were misleading and they have been changed https://github.com/laravel/docs/pull/5096.
If detach method called without ids (for detach all relations), events are not firing
https://github.com/laravel/framework/pull/27571#issuecomment-493451259
i tried many different way for the solve this need, but it is impossible without use external package or override many method.
I choose chelout/laravel-relationship-events package.
It's look clean and understable. And use with trait.
I'm using ajax to update my model User, the ajax part works fine since the data is updated successfully in the database, inside my controller action the update performed by :
$user->update($data);
The part that doesn't work:
I've used boots method updated inside my model like :
class User extends BaseModel
{
...
public static function boot()
{
parent::boot();
self::updated(function($model){
Log::info("updated");
dd($model);
});
}
}
The event was never reached I'm not sure why.
Problem:
I'm trying to perform an action after the model update but the event doesn't fire.
Here's what the manual states with update()
When issuing a mass update via Eloquent, the saved and updated model events will not be fired for the updated models. This is because the models are never actually retrieved when issuing a mass update.
You need to use save to trigger events. Something like:
$user->fill($data);
$user->save();
This of course is assuming that $user is a model and not a query builder instance.
You are accessing the function statically:
instead try using
self::updated(function($model){
Log::info("updated");
dd($model);
});