[Help] I need when deleting a record it will also create a deleted_by value.
[company table]
| name | created_by | updated_by | deleted_by | created_at | updated_at | deleted_at |
public static function boot()
{
static::creating(function($company)
{
$company->created_by = Auth::user()->id;
});
}
This will fill created by with the user id when we creating record.
But when I use this when delete record ( using soft delete )
public static function boot()
{
parent::boot();
static::deleting(function($company)
{
$company->deleted_by = Auth::user()->id;
});
}
this method is doesn't work, deleted_by column is not update.
How to fill deleted_by with user id when we delete a record ?
Thanks before
At the moment, Laravel's delete() method does not take other attribute changes into consideration when it creates the relevant UPDATE query. Here's a snippet from Eloquent\Model::performDeleteOnModel():
$query = $this->newQuery()->where($this->getKeyName(), $this->getKey());
if ($this->softDelete)
{
$this->{static::DELETED_AT} = $time = $this->freshTimestamp();
$query->update(array(static::DELETED_AT => $this->fromDateTime($time)));
}
else
{
$query->delete();
}
Because of the newQuery(), no changes made to the object are taken into consideration when building the actual DB query.
You could extend the Model class and add in your own functionality to accept any changed attributes, or you can simply toss $company->save() in your static::deleting() closure. It will perform two queries, but will take you mere seconds to implement, compared to the first option. Your choice!
Related
My client has an old site built on 4.x that I'm trying to get to work with 7.4. I have most of it working, but am stuck on a belongsToMany relationship
I have a Manufacturer class that has a many-to-many relationship with Subcategories, through a table named membercategory. However, the subcategories property always returns an empty array. What am I missing?
membercategory
+------+------------------+-----------------+
| ID | FKManufacturerID | FKSubCategoryID |
+------+------------------+-----------------+
| 3203 | 24 | 301 |
| 3202 | 24 | 292 |
| 3201 | 24 | 295 |
+------+------------------+-----------------+
and my Manufacturer class
class Manufacturer extends Model {
public function subcategories() {
# have tried swapping the two FK parameters, same result
return $this->belongsToMany('App\Subcategory','membercategory','FKSubCategoryID','FKManufacturerID');
}
}
I'm testing it in my controller using this
dd($manufacturers[0]->subcategories);
where $manufacturers[0] returns the object for Manufacturer ID=24
According to the documentation, your code should look like this:
class Manufacturer extends Model {
public function subcategories() {
return $this->belongsToMany('App\Subcategory','membercategory','FKManufacturerID','FKSubCategoryID');
}
}
Looking at the belongsToMany signature:
public function belongsToMany(
$related,
$table = null,
$foreignPivotKey = null,
$relatedPivotKey = null,
$parentKey = null,
$relatedKey = null,
$relation = null) {}
You will see that you need to have the related column in the 4th param and the foreign one in the 3rd.
EDIT
Make sure the types of columns in the model tables and the pivot table match.
Furthermore, according to the belongsToMany signature, you should add the parentKey and relatedKey params if they are not the default: id ( ID != id )
Here is a list of eloquent model conventions in Laravel 7.x and you can see:
Eloquent will also assume that each table has a primary key column named id. You may define a protected $primaryKey property to override this convention:
That means that provided both your model tables have the primary key columns named "ID" ( as you showed you do in the chat ) your code should look like this:
class Manufacturer extends Model {
public function subcategories() {
return $this->belongsToMany(
'App\Subcategory',
'membercategory',
'FKManufacturerID',
'FKSubCategoryID'
'ID',
'ID'
);
}
}
Just replace the positions of third and fourth columns like this:
return $this->belongsToMany('App\Subcategory', 'membercategory', 'FKManufacturerID', 'FKSubCategoryID');
I have Games table and which has the following schema
id | status | name
status column has 2 values (Active, Pending)
And GamePlayer table which has the following schema
id | game_id | player_id | request_status
request_status column has 3 values (Pending, Confirm, Rejected)
Now I have to select all game in which the player is involved but with the following constraints:
If the game is Pending state then it will be shown to all game_players
If the game is in Active state then it will be only shown to the game_player whose request_status is Confirm.
Game(Model)
public function GamePlayer()
{
return $this->hasMany('App\Models\GamePlayer', 'game_id', 'id');
}
public function getGames($playerId)
{
$gameList = Game::with(['GamePlayer','Category:id,name'])
->whereHas('GamePlayer', function ($q) use ($playerId) {
$q->where('player_id', $playerId);
})->get();
return $gameList;
}
Controller
$this->gameObj = new Game();
$gameList = $this->gameObj->getGames($player_id);
Please help me out how can I populate data from another table based on condition(parent table as well as the child).
You can use condition in your relationship to get only confirmed game players
public function activeGamePlayers()
{
return $this->hasMany('App\Models\GamePlayer', 'game_id', 'id')
->where('request_status', 'confirm');
}
You can as well use scopes (https://laravel.com/docs/7.x/eloquent#local-scopes) to select only active games
public function scopeActive($query)
{
return $query->where('status', 'active');
// You can then use $game->active()->...
}
So your getGame would look like:
public function getGames($playerId)
{
$player = GamePlayer::find($playerId);
if ($player->status == 'active') {
$gameList = Game::with(['GamePlayer', 'Category:id,name'])
->get();
} else {
$gameList = Game::with(['GamePlayer', 'Category:id,name'])
->active()->get();
}
return $gameList;
}
NOTE:
in your specific case, I would instead get started from the GamePlayer Model to get the games, instead of coming from the Game Model - you can use scopes and conditions in relationships as well to make your code more readable.
You can use Laravel method whereHas. You should read this part of Laravel the documentation.
Note that you should have relationships declared on every model.
I have a setup where, I have a user_roles table.
Then the administrator, will be able to update these, by adding new role, updating the name of an old role, and lastly delete.
------------------
| id | user_role |
| 1 | admin |
| 2 | role 1 |
| 3 | role 3 |
------------------
I tried to use this block of code to update, add, delete the roles
public function update(Request $request, $id){
$roles = Model::all();
foreach($roles as $role){
$role->delete();
}
foreach($request->role as $role){
$role = new Model;
$role->user_role = $role;
$role->save();
}
}
But in the users table, I have a user_role column that references to the id of the user_roles table, so whenever I use the above code, all rows will be deleted, then a new id will be created for the new rows inserted. So, when I go back to the list of users, their user_role column will return null, because each user has a non-existing user_role id.
Using updateOrCreate kind of solves my problem, when it comes to updating old row/s or creating new row/s but the problem is, deleting rows. It can't delete rows.
I need to delete the rows that are existing in the database, and missing in the request.
What would be the best way to do this ?
I have fixed this case using the id approach proposed by maslauskast # https://laracasts.com/discuss/channels/laravel/updateorcreate-rows-from-a-csv-or-delete-if-not-on-the-csv?page=0
In your case a solution* will be
public function update(Request $request, $id){
$usersRoleToDelete = Model::all()->pluck('id', 'id');
foreach($request->role as $role) {
$createdOrUpdated = Model::createOrUpdate(['user_role' => $role]);
if (!empty($usersRoleToDelete[$createdOrUpdated->id])) {
unset($usersRoleToDelete[$createdOrUpdated->id]);
}
}
if (count($usersRoleToDelete)) {
Model::whereRaw(sprintf('id IN (%s)', implode(',', $usersRoleToDelete)))->delete();
/* Alternatively you could use
* Model::whereIn('id', $usersRoleToDelete)->delete();
* if the amount of ids is smaller than the maximum PDO arguments allowed
*/
}
}
Please note that I have not tested this.
this is my working updateOrCreate method based on Adrian answer in Laravel 5.3
public function update($id, Request $request)
{
$et=Model::find($id);
//update some parent table field's (if you want)
$et['type']=$request['type'];
$et['sum'] =preg_replace('/[^0-9]/s', '', $request['sum']); //for remove thousand separtor
$et->save();
$et_det=$et->et_dets()
->pluck('id','id'); // one-to-many relation
for ($i=0;$i<count($request['det_id']);$i++)
{//some matching condition
$createdOrUpdated = Model_dets::updateOrCreate(
[
'parent_id'=>$id,
'total'=>$request['total'][$i]
],
[
'columnA' => $request['columnA'][$i],
'ColumnB' => $request['ColumnB'][$i])
]);
if (!empty($et_det[$createdOrUpdated->id])) {
unset($et_det[$createdOrUpdated->id]);
}
}
if (count($et_det)) {
Model_det::whereIn('id', $et_det)->delete();
}
return Redirect::to('index')->with('alert-success','Update successfully');
}
I have 2 tables related by a polimorphic relationship:
The Store table:
id | name | ...
15 | my_store | ...
And the Tag table, that is connected to the Store table through the related_id key and the type.
id | related_id | tag | created | type
1 | 15 | test | 00:00:00 | store
2 | 15 | dummy | 00:00:00 | product
So, in that example, the Store "my_store" only has the tag "test" ("dummy" is a tag for a product, not a Store, besides it has the same related_id).
I have the Store model defined in my code as follows:
class Store extends Model {
protected $table = 'store';
public function tags()
{
return $this->morphMany('App\Tag', 'related', 'type');
}
}
And the tag model:
class Tag extends Model {
protected $table = 'tags';
public function related()
{
return $this->morphTo();
}
}
But when I try to run
$store->tags;
I saw that the query that Laravel is trying to run is:
SQL: select * from `mwp_tags` where `mwp_tags`.`related_id` = 46
and `mwp_tags`.`related_id` is not null
and `mwp_tags`.`type` = App\Store)
The query is looking for App\Store instead of store
I cannot change the value in DB and I wouldn't like to use the
protected $morphClass = 'store';
inside the Store model as I don't know if a would have to create another morphMany relationship, probably with another type name.
Somebody knows how to skip this issue? Thanks!
You could try something like this:
public function tags()
{
$default_morph_class = $this->morphClass ?: null;
$this->morphClass = 'store';
$morph_many_query = $this->morphMany('App\Tag', 'related', 'type');
$this->morphClass = $default_morph_class;
return $morph_many_query;
}
The idea being you change the morphClass on the fly and set it back. That allows you to use a different morphClass for a different relationship.
Let I have a table named customer where customer table has a field named deleted_by.
I implement softDelete in customer model. Now I want to update deleted_by when row delete. So that I can trace who delete this row.
I do search on google about it But I don't found anything.
I use laravel 4.2.8 & Eloquent
You may update the field using something like this:
$customer = Customer::find(1); // Assume 1 is the customer id
if($customer->delete()) { // If softdeleted
DB::table('customer')->where('id', $customer->id)
->update(array('deleted_by' => 'SomeNameOrUserID'));
}
Also, you may do it in one query:
// Assumed you have passed the id to the method in $id
$ts = Carbon\Carbon::now()->toDateTimeString();
$data = array('deleted_at' => $ts, 'deleted_by' => Auth::user()->id);
DB::table('customer')->where('id', $id)->update($data);
Both is done within one query, softDelete and recorded deleted_by as well.
Something like this is the way to go:
// override soft deleting trait method on the model, base model
// or new trait - whatever suits you
protected function runSoftDelete()
{
$query = $this->newQuery()->where($this->getKeyName(), $this->getKey());
$this->{$this->getDeletedAtColumn()} = $time = $this->freshTimestamp();
$deleted_by = (Auth::id()) ?: null;
$query->update(array(
$this->getDeletedAtColumn() => $this->fromDateTime($time),
'deleted_by' => $deleted_by
));
}
Then all you need is:
$someModel->delete();
and it's done.
I would rather use a Model Event for this.
<?php
class Customer extends \Eloquent {
...
public static function boot() {
parent::boot();
// We set the deleted_by attribute before deleted event so we doesn't get an error if Customer was deleted by force (without soft delete).
static::deleting(function($model){
$model->deleted_by = Auth::user()->id;
$model->save();
});
}
...
}
Then you just delete it like you would normally do.
Customer::find(1)->delete();
I know this is an old question, but what you could do (in the customer model) is the following....
public function delete()
{
$this->deleted_by = auth()->user()->getKey();
$this->save();
return parent::delete();
}
That would still allow the soft delete while setting another value just before it deletes.