Eloquent many to many to many record attach - laravel

I have two tables.
message - id, title, content
user - id, name
I have a pivot table.
message_user - message_id, user_id_sender, user_id_receiver
A message can be sent by 1-0 (sender can be null) users, defined with user_id_sender
A message can be received by 1-N users, defined with user_id_receiver
I am trying to define this in Eloquent.
The only solution I have is:
class Message
public function users()
{
return $this->belongsToMany(
'User',
'message_user',
'user_id_sender',
'user_id_receiver');
}
// Somewhere in the code
$message = $this->messageRepository->store($message);
$message->users()->attach($message->id, array(
'message_id' => $message->id,
'user_id_sender' => $userSender->id,
'user_id_receiver' => $userReceiver->id
));
This is the only solution I could think of, but it feels like a workaround. Is there a proper way to achieve this?

You could put into your Message model this:
public function betweenUsers($sender, $receiver) {
return $this->users()->attach($this->id, [
'message_id' => $this->id,
'user_id_sender' => $sender->id,
'user_id_receiver' => $receiver->id,
]);
}
Then you could somewhere in the code do $message->betweenUsers($userSender, $userReceiver);
I understand it is not a exactly what you wanted, but since you are really uncommon kind of pivot table, this is probably sufficient too.

Related

Returning a Modal with relationships after creating in Laravel

I have a modal for example User. The User has relationships to Country and Currency modal as well. I would like to return a JSON object of the User with their relationships. I can achieve that using the code below:
$user = User::create($request->all()); // request contains all the information to create the user
$userDetails = User::with('country', 'currency')->where('id', $user->id)->first();
return json_encode($userDetails);
It works. However, is there a better or more recommended way to achieve this? Thank you for your time.
I found out that creating a new Laravel Resource and using the following approach seems much cleaner:
// in UserResource.php:
public function toArray($request)
{
return [
'id' => $this->id,
'country' => $this->country, // relationship
'currency' => $this->currency // relationship
]
}
// In controller:
return new UserResource($user);
Thanks!

How does Laravel generate SQL?

I'm brand new to Laravel and am working my way through the Laravel 6 from Scratch course over at Laracasts. The course is free but I can't afford a Laracasts membership so I can't ask questions there.
I've finished Section 6 of the course, Controller Techniques, and am having unexpected problems trying to extend the work we've done so far to add a few new features. The course has students build pages that let a user show a list of articles, look at an individual article, create and save a new article, and update and save an existing article. The course work envisioned a very simple article containing just an ID (auto-incremented in the database and not visible to the web user), a title, an excerpt and a body and I got all of the features working for that. Now I'm trying to add two new fields: an author name and a path to a picture illustrating the article. I've updated the migration, rolled back and rerun the migration to include the new fields and got no errors from that. (I also ran a migrate:free and got no errors from that.) I've also updated the forms used to create and update the articles and added validations for the new fields. However, when I go to execute the revised create code, it fails because the SQL is wrong.
The error message complains that the author field doesn't have a default, which is true, I didn't assign a default. However, I did give it a value on the form. What perplexes me most is the SQL that it has generated: the column list doesn't show the two new columns. And that's not all: the values list is missing apostrophes around any of the string/text values. (All of the columns are defined as string or text.)
As I said, I'm completely new to Laravel so I don't know how to persuade Laravel to add the two new columns to the Insert statement nor how to make it put apostrophes around the strings in the values list. That hasn't come up in the course and I'm not sure if it will come up later. I was hoping someone could tell me how to fix this. All of my functionality was working fine before I added the two new fields/columns.
Here is the error message:
SQLSTATE[HY000]: General error: 1364 Field 'author' doesn't have a default value (SQL: insert into `articles` (`title`, `excerpt`, `body`, `updated_at`, `created_at`) values (Today in Canada, The ideal winter-beater, This car is the ideal winter-beater for the tough Canadian climate. It is designed to get you from A to B in style and without breaking the bank., 2020-02-15 17:37:54, 2020-02-15 17:37:54))
Here is ArticlesController:
<?php
namespace App\Http\Controllers;
use App\Article;
use Illuminate\Http\Request;
class ArticlesController extends Controller
{
public function index()
{
$articles = Article::latest()->get();
return view ('articles.index', ['articles' => $articles]);
}
public function show(Article $article)
{
return view('articles.show', ['article' => $article]);
}
public function create()
{
return view('articles.create');
}
public function store()
{
//Stores a NEW article
Article::create($this->validateArticle());
return redirect('/articles');
}
public function edit(Article $article)
{
return view('articles.edit', ['article' => $article]);
}
public function update(Article $article)
{
//Updates an EXISTING article
$article->update($this->validateArticle());
return redirect('/articles/', $article->id);
}
public function validateArticle()
{
return request()->validate([
'title' => ['required', 'min:5', 'max:20'],
'author' => ['required', 'min:5', 'max:30'],
'photopath' => ['required', 'min:10', 'max:100'],
'excerpt' => ['required', 'min:10', 'max:50'],
'body' => ['required', 'min:50', 'max:500']
]);
}
public function destroy(Article $article)
{
//Display existing record with "Are you sure you want to delete this? Delete|Cancel" option
//If user chooses Delete, delete the record
//If user chooses Cancel, return to the list of articles
}
}
Is there anything else you need to see?
It may be possible because of you don't have defined that column in fillable property, to use mass assignment you have to specify that columns.
Try after adding that columns in fillable property.
Laravel mass assignment
Hope this helps :)

Relationship User and children data using belongsTo

public function children()
{
return $this->belongsTo('App\User', 'parent_id', 'id');
}
$item = Item::where([
'status' => '1',
'id' => $id
])->first();
$user = User::where([
id=>Auth::id()
])->with('children')->get()
return view('item.list')->with(compact('item', 'user'));
How can I get item and authenticated user along with children and send it to view in one query or is there any other best practise.
From your code, it looks like Item is not related in any way to the User model. If that is the case, there is likely no reason to try to do this in one query, and likely not efficient or even possible. There could be some relation that I don't know of, but you are probably better off doing the two queries.. One for the $item:
$Item = Item::where([
'status' => '1',
'id' => $id
])->first();
And then one to load the children on the User object. This one is potentially a little different. If you are manually loading the User object for some reason, you can eager load the children:
$user = User::where([
'id' => $someId
])->with('children')->first();
If you are within the auth middleware, you don't need to re-load the user, it is already provided by Laravel. You can just load the children if they are not already there:
$user->load('children');
or, even easier, from your blade page, just call the load on the page:
$user->children();
You have the code correct for sending to your view:
return view('item.list')->with(compact('item', 'user'));

Laravel: Eloquent how to update a model and related models in one go

Does anyone know if it is possitble to do the folowing:
Let's say we have a model called User and a model calledd BestFriend. The relation between the User and the best friend is 1:1.
I would like for these cases be able to do something like this, change my city and the city of my friend at the same time.
$me = User::find(1);
$me->update(array(
'city' => 'amsterdam',
'bestfriend.city' => 'amsterdam'
));
So basically I would like to know if Eloquent is smart enough to understand the relationship based on the array key 'bestfriend.city'.
Thanks in advance for any help!
Update:
Found the solution on the Laravel forums but im posting it here as well if someone else is looking for the same thing :)
In the model you add
// In your model...
public function setBestFriendArrayAttribute($values)
{
$this->bestfriend->update($values);
}
And then you can call it like this
$me->update(array(
'city' => 'amsterdam',
'BestFriendArray' => array(
'city' => 'amsterdam'
)
));
Works like a charm!
You don't need to set it on your model. You can do it on your controller like this.
$me = User::find(1)->bestFriend()->update(array(
'city' => 'amsterdam',
'bestfriend.city' => 'amsterdam'
));
I just modified your update a little bit.
Eloquent is pretty smart, but I don't believe it can do that. You would have to update User and BestFriend independently. But once you've done that, Eloquent does have methods for attaching the two.
$me = User::find(1);
$bff= BestFriend::find(1);
$me->city = 'amsterdam';
$bff->city = 'amsterdam';
$me->bestfriend()->associate($bff);
This is of course assuming your User model has a function that looks like...
public function bestfriend()
{
return $this->hasOne('BestFriend');
}
If you want to save same city name to both entities this will do the job:
$me = User::find(1);
$me->update(array(
'city' => 'amsterdam',
));
And in your model:
public function setCityAttribute($value)
{
$this->attributes['city'] = $value;
$this->bestfriend->city= $value;
$this->bestfriend->save();
}

Laravel Seeding Does not Fill in Fields

I have a database seed file:
class ContactTableSeeder extends Seeder {
public function run()
{
$contacts = array(
array(
'first_name' => 'Test',
'last_name' => 'Contact',
'email' => 'test.contact#emai.com',
'telephone_number' => '0111345685',
'address' => 'Address',
'city' => 'City',
'postcode' => 'postcode',
'position' => 'Director',
'account_id' => 1
)
);
foreach ($contacts as $contact) {
Contact::create($contact);
}
}
}
When I run php artisan migrate:refresh --seed it seeds the database and creates the relevant record in the contacts table, except that it does not fill the fields with any of the information in the seed array. I am using the exact same syntax for other tables and they work fine, and I've also checked each field thoroughly to make sure they match the database fields but no matter what I do it will not seed correctly.
Does anyone have any ideas?
I had this same problem but none of the above solutions worked for me. It turned out to be due to having a construct function in my model! After I removed this it worked fine!
public function __construct()
{
parent::__construct();
}
EDIT:
After further reading on this I discovered the issue is due to the fact that if you are going to include a constructor in your model must accept the attributes parameter and pass it to the parent. If you do this then the constructor does not break the DB seeding (and probably other things). I hope this saves someone else a headache.
public function __construct($attributes = array())
{
parent::__construct($attributes);
}
Turns out the issue was to do with relationships in my models.
For future visitors to this question: make sure to check all the functions in your models that define hasOne/hasMany/etc relationships. Read though the Eloquent docs for more.
Do you have $this->call("ContactTableSeeder") in your DatabaseSeeder class' run() function?
If you have ContactTableSeeder in it's own file, is the file named ContactTableSeeder.php exactly? If not it would fail to load according to the PSR-0 Standard.
These are my first thoughts.
Have you tried to replace the following lines:
foreach ($contacts as $contact) {
Contact::create($contact);
}
with
DB::table('contact')->insert($contacts);
assuming your table name is contact.
And also, make sure you have line like this
$this->call('ContactTableSeeder');
in your DatabaseSeeder class.

Resources