45

I am new to laravel. I am working on a laravel 5 app and I am stuck here. I have 2 models as such:

class Message extends Eloquent{

    public function user()
    {
        return $this->belongsTo('App\User', 'from');
    }

    public function users()
    {
        return $this->belongsToMany('App\User')->withPivot('status');
    }
}

class User extends Eloquent {

    public function messages()
    {
        return $this->hasMany('App\Message', 'from');
    }

    public function receive_messages() {
        return $this->belongsToMany('App\Message')->withPivot('status');
    }
}

There exist a many-to-many relationship between Message and User giving me a pivot table as such:

Table Name: message_user
Colums:
message_id
user_id
status

I have an SQL query as such:

update message_user
set status = 1
where user_id = 4 and message_id in (select id from messages where message_id = 123)

How can I translate this query to the laravel equivalent?

Fokwa Best
  • 3,322
  • 6
  • 36
  • 51
  • Dude what do you mean by `from`? – Mohamed Mo Kawsara Nov 05 '15 at 16:31
  • Foreign key @Mohamed – Fokwa Best Nov 05 '15 at 17:34
  • dude the second argument is the conjunction table name, in your example it should be 'message_user' , the total right relation for **messages** in **User** model : `return $this->hasMany('App\Message\,'message_user','user_id','message_id');` – Mohamed Mo Kawsara Nov 05 '15 at 18:07
  • No dude. You are missing the point. Check out the laravel documentation. This can help: http://laravel.com/docs/5.1/eloquent-relationships#many-to-many – Fokwa Best Nov 05 '15 at 22:42
  • @Mohamed the docs has something like so: return $this->hasMany('App\Comment', 'foreign_key', 'local_key'); – Fokwa Best Nov 05 '15 at 22:49
  • buddy I just checked it, I'm sure about it, in same link that you've send me search for this line please: `return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'role_id');` – Mohamed Mo Kawsara Nov 07 '15 at 07:30
  • @Mohamed `from` is on `return $this->belongsTo('App\User', 'from');` on my post. Look at the post again. It's on the 1 to many relationship. – Fokwa Best Nov 07 '15 at 14:21
  • Ok I got, sorry, now could you explain how do you want one to many and many to many relationships between same two tables? `users` , `messages` – Mohamed Mo Kawsara Nov 08 '15 at 09:15
  • 1
    Thanks for the input dude. A user can create 1 or more messages. A message can be received by 1 or more users. A user can receive 0 or more messages. – Fokwa Best Nov 09 '15 at 04:57

5 Answers5

76

The code below solved my problem:

$messages  = Message::where('message_id', $id)->get();
foreach($messages as $message)
   $message->users()->updateExistingPivot($user, array('status' => 1), false);
Fokwa Best
  • 3,322
  • 6
  • 36
  • 51
20

You may use one of these two functions, sync() attach() and the difference in a nutshell is that Sync will get array as its first argument and sync it with pivot table (remove and add the passed keys in your array) which means if you got 3,2,1 as valued within your junction table, and passed sync with values of, 3,4,2, sync automatically will remove value 1 and add the value 4 for you. where Attach will take single ID value

The GIST: if you want to add extra values to your junction table, pass it as the second argument to sync() like so:

$message = Messages::find(123);
$user = User::find(4);

// using attach() for single message
$user->message()->attach($message->id, [
    'status' => 1
]);



$message2 = Messages::find(456); // for testing

// using sync() for multiple messages
$user->message()->sync([
    $message->id => [
        'status' => 1
    ],
    $message2->id => [
        'status' => 1
    ],
]);
Mohamed Mo Kawsara
  • 4,400
  • 2
  • 27
  • 43
  • 1
    Thanks for your input @Mohamed. Sync didn't do the trick for me. – Fokwa Best Nov 09 '15 at 05:01
  • @FokwaBest dude based on your comment above, you want only many to many relationship, therefore there is a misleading with your eloquent relations, if you want I can update my answer and write the right relations and after that `sync()` will work :) – Mohamed Mo Kawsara Nov 09 '15 at 08:50
  • dude actually when a user creates a message, is stored in the messages table. The pivot table just keep a record of the message_id and the recipient ($user_id). It's working pretty well now but I would love to see your approach anyway. – Fokwa Best Nov 09 '15 at 10:11
  • 2
    Thanks @MohamedKawsara! I just want to notice everybody the most important `false` flag in `sync()` method call. Because if it's not passed, all other records different than current $message->id will be detached. Also, notice that when we attach, method call is `attach( $id, ['attr1' => 'val1'])` but at `sync()` call we have ```sync([$id **=>** ['attr1' => 'val1']]);``` – М.Б. Dec 25 '17 at 15:46
8

Here is a small example of how to update the pivot table column

    $query = Classes::query();
    $query = $query->with('trainees')
                   ->where('user_id', Auth::id())
                   ->find($input['classId']);

    foreach ($query->trainees as $trainee) {
       $trainee->pivot->status = 1 //your column;
       $trainee->pivot->save();
    }

Note: make sure your relation data must in an array Hope its help you :) happy coding

Parth kharecha
  • 6,135
  • 4
  • 25
  • 41
6

Laravel 5.8

First, allow your pivot columns to be searchable by chaining the withPivot method to your belongsToMany

Copied from my own code to save time

// I have 3 columns in my Pivot table which I use in a many-to-many and one-to-many-through scenarios

$task = $user->goalobjectives()->where(['goal_objective_id'=>$goal_objective_id,'goal_obj_add_id'=>$goal_obj_add_id])->first(); //get the first record

$task->pivot->goal_objective_id = $new; //change your col to a new value

$task->pivot->save(); //save

The caveat is that your pivot table needs to have a primary 'id' key.

If you don't want that then you can try the following:

$tasks=$user->posts()->where(['posts_id'=>$posts_id,'expires'=>true])->get()->pluck('id'); // get a collection of your pivot table data tied to this user

$key=join(",",array_keys($tasks->toArray(),$valueYouWantToRemove));

$tasks->splice($key,1,$newValueYouWantToInsert);

$c = array_fill(0,$tasks->count(),['expires'=>true]); //make an array containing your pivot data

$newArray=$tasks->combine($c) //combine the 2 arrays as keys and values

$user->posts()->sync($newArray); //your pivot table now contains only the values you want

4th July Update Update to above snippet.

//Ideally, you should do a check see if this user is new
//and if he already has data saved in the junction table
//or are we working with a brand new user

$count = $user->goalobjectives->where('pivot.goal_obj_add_id',$request->record)->count();

//if true, we retrieve all the ids in the junction table 
//where the additional pivot column matches that which we want to update

if($count) {

$ids = $user->goalobjectives->where('pivot.goal_obj_add_id',$request->record)->pluck('id');

//convert to array

$exists = $ids->toArray();

//if user exists and both saved and input data are exactly the same
//there is no need
//to update and we redirect user back

if(array_sum($inputArray) == array_sum($exists)) {

//redirect user back

}

//else we update junction table with a private function
//called 'attachToUser'

$res = $this->attachToUser($user, $inputArray, $ids, $request->record);

}//end if

elseif(!$count) {

//we are working with a new user
//we build an array. The third pivot column must have equal rows as
//user input array

$fill = array_fill(0,count($inputArray),['goal_obj_add_id'=>$request->record]);

//combine third pivot column with user input

$new = array_combine($inputArray,$fill);

//junction table updated with 'user_id','goal_objective_id','goal_obj_add_id'

$res = $user->goalobjectives()->attach($new);

//redirect user if success

}

//our private function which takes care of updating the pivot table

private function attachToUser(User $user, $userData, $storedData, $record) {

//find the saved data which must not be deleted using intersect method

$intersect = $storedData->intersect($userData);

if($intersect->count()) {

//we reject any data from the user input that already exists in the database

$extra = collect($userData)->reject(function($value,$key)use($intersect){

return in_array($value,$intersect->toArray());

});

//merge the old and new data

$merge = $intersect->merge($extra);

//same as above we build a new input array

$recArray = array_fill(0,$merge->count(),['goal_obj_add_id'=>$record]);

//same as above, combine them and form a new array

$new = $merge->combine($recArray);

//our new array now contains old data that was originally saved
//so we must remove old data linked to this user
// and the pivot record to prevent duplicates

$storedArray = $storedData->toArray();

$user->goalobjectives()->wherePivot('goal_obj_add_id',$record)->detach($storedArray);

//this will save the new array without detaching
//other data previously saved by this user

$res = $user->goalobjectives()->wherePivot('goal_obj_add_id',$record)->syncWithoutDetaching($new);

}//end if

//we are not working with a new user
//but input array is totally different from saved data
//meaning its new data

elseif(!$intersect->count()) {

$recArray = array_fill(0,count($userData),['goal_obj_add_id'=>$record]);

$new = $storedData->combine($recArray);

$res = $user->goalobjectives()->wherePivot('goal_obj_add_id',$record)->syncWithoutDetaching($new);

}

//none of the above we return false

return !!$res;

}//end attachToUser function

This will work for pivot table which doesn't have a primary auto increment id. without a auto increment id, user cannot update,insert,delete any row in the pivot table by accessing it directly.

Bruce Tong
  • 1,366
  • 15
  • 14
3

For Updating your pivot table you can use updateExistingPivot method.

Babar Sajjad
  • 195
  • 1
  • 4
  • Thanks @Babar. Can you add any sample code on how I may use the method to come with the query on the post. – Fokwa Best Nov 05 '15 at 12:58
  • You can see documentation at http://laravel.com/docs/5.0/eloquent#working-with-pivot-tables Reference code to do so is User::find(1)->roles()->updateExistingPivot($roleId, $attributes); – Babar Sajjad Nov 05 '15 at 13:08