How to create a data associatively? laravel eloquent - laravel

I have a Post model:
class Post extends Model
{
protected $fillable = [
'title',
'user_id',
'token',
'body'
];
public function favorites()
{
return $this->hasMany(Favorite::class);
}
public function addFavorite($state = 1)
{
$this->favorites()->create(compact('state'));
}
}
Favorite model:
class Favorite extends Model
{
protected $fillable = ['user_id', 'post_id', 'state'];
}
When I test in tinker:
$post = Post::first();
$post->addFavorite();
It returns me an error below:
Illuminate/Database/QueryException with message 'SQLSTATE[HYOOO]: General error: 1364 Field 'user_id' doesn't have a default value (SQL: insert into favorites...
Why it ask user_id when it is given in the post? Question is do I necessarily need to input the user_id to achieve this?

The question of whether user_id is necessary is up to you. Will it come in handy later on? Does having it on the posts table suffice?
It is asking for user_id because you do not have a default value field on that field in the favorites table. You can either set a default value, remove it from the table (if you decide you don't need it), OR provide it when creating via the relationship:
class Post extends Model
{
protected $fillable = [
'title',
'user_id',
'token',
'body'
];
public function addFavorite($state = 1)
{
$this->favorites()->create([
'state' => $state,
'user_id' => $this->user_id,
]);
}
public function removeFavorite()
{
$this->addFavorite(0);
}
}
Don't forget to include the relationship definition of favorites on the Post model.
Based on the plural name, it seems that a post has many favorites, but then your removeFavorite() method calls the addFavorite method?? This would not actually remove anything - it would create a new record.

Since Favorite model is related to Post model and you create it via relation()->create(), you can skip specifying post_id as Laravel can deduce it. But you do have to specify user_id, because there's no way for your code to know that favourite.user_id and post.user_id is the same. So in short: yes, you have to specify user_id.

Related

Laravel getAttribute() on eloquent?

so i just wondered, if something like this is possible, since my code does not work.
protected $appends = ['position_name'];
public function getPositionNameAttribute()
{
return $this->belongsTo('App\EmployeePosition', 'employee_position_id')->name;
}
Can I append the name of Eloquen relationship model?
edit: so far, i am using this:
foreach ($employees as $e) {
$e->position_name = $e->position->name;
}
So, I needed to use the relation defined before.
protected $appends = ['position_name'];
public function position()
{
return $this->belongsTo('App\EmployeePosition', 'employee_position_id');
}
public function getPositionNameAttribute()
{
return $this->position->name;
}
Based on your comments i'd suggest to use the laravel default solution for your problems API resrouces
eg
class EmployeeResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'position_name' => $this->position->name,
];
}
}
note: using the with as other people suggested to preload information can increase performance by reducing the amount of queries, if you are returning a collection of employees.
Creating an accessor that looks up a value in another model and appending this field by using $appends is bad practice and will lead to n+1 queries whenever you fetch your Employee model. You should avoid doing this and just use $employee->position->name.
You should also make sure to use Employee::with('position') when you need to show the position name, so that the position model is fetched in a single query.
If the position name is something that you need in all your Employee queries, then you can set the Employee to always eager load the position by defining the following inside your Employee model:
/**
* The relationships that should always be loaded.
*
* #var array
*/
protected $with = ['position'];
I think you can just create a model with position names and reference it to the position id in the other mode by using eloquent relationships.

Laravel class isn't in polymorphic relationship with other class

I am working on a polymorphic relationship between these classes in my small project: Submissions (same as posts), Comments and Users.
The problem is, I am able to gather all comments by relying on Submission model, for example:
$submissions = Submission::with('comments')->get()->find( $submission );
But, if I try something like this in tinker:
$users = User::with( 'comments' )->get();
I get all the users data, but no comments show up:
comments: Illuminate\Database\Eloquent\Collection {#3003
all: [],
},
Here's all the code of relations between models:
class Comment extends Model
{
protected $fillable = [ 'commentable_id', 'commentable_type', 'text' ];
public function commentable() {
return morphTo();
}
}
Now this is being added inside Submission and User model:
public function comments() {
return $this->morphMany( Comment::class, 'commentable' );
}
In Submission model I am using protected $guarded = []; to deal with Mass Assignment and I don't call it inside user model.
I hope you can tell me what am I doing wrong since somehow I can't access user model by comment model and vice versa.
Thanks in advance!
edit: Here's a code of creation of a comment:
public function store( Submission $submission ) {
$data = request()->validate([
'text' => [ 'required', 'string', 'max:255' ]
]);
$id = $submission->comments()->create([ 'text' => request()->text ]);
dd( $submission, $data );
}
Relation morphMany is one-to-many relation, like hasMany. So one comment can belong to a submission or a user, not both. One option is to create separate comments for submission and user (for the user do the same as for submission $user->comments()->->create([...])). Second option is to use many-to-many polymorphic relation, where one comment can belong to multiple submissions and users.

Can eloquent ignore irrelevant data in Laravel 5.7

This is basically the same question as this here from 2013. Except the answer doesn't work for me.
I have a Model App\Post:
class Post extends Model
{
protected $fillable = ['title'];
// This Model doesn't contain an 'authorname' field
public function author()
{
return $this->belongsTo('App\Author');
}
}
and a Model App\Author:
class Author extends Model
{
protected $fillable = ['name'];
public function posts()
{
return $this->hasMany('App\Post');
}
}
And an array I want to save to that Model:
$posts = [
['title'=>'one post', 'authorname' => 'Mickey'],
['title'=>'another post', 'authorname' => 'Minny'],
];
foreach($posts as $post){
$authorModel=App\Author::firstOrCreate(['name'=>$post['authorname']]);
App\Post::create($post)->author()->associate($authorModel)->save();
}
According to this question, that should work, but I get an
SQL error 42522: Column not found: 1054 Unknown column 'authorname' in 'field list'
which suggests Laravel forwards the whole array to mySQL. Is there a way to make this work without unsetting the authorname key?
Obviously this is a simpified version of what I want to do and keeping track of what to unset seems unnecessary - as would be assigning all array keys to their respective database fields manually.
The only idea I have here is that you run this code in DatabaseSeeder (which automatically unguards models) or you somewhere manually call Eloquent::unguard() (or code similar to this). This would explain why any other fields are used when creating model no matter of $fillable property.

Passing data from controller into Model in Laravel

I have this comment controller:
class CommentsController extends Controller
{
public function store(Article $article){
$comment = new Comment();
$comment->user_id = auth()->id();
$comment->comment = request('comment');
$comment->article_id = $article->id;
$comment->save();
return back();
}
}
And it works. I tried to make my code cleaner by putting that logic into my model. So I changed it like this:
class CommentsController extends Controller
{
public function store(Article $article){
$article->addComment(request('comment'));
return back();
}
}
Meanwhile, in my Comment Model I'm doing like this:
class Article extends Model
{
protected $fillable = ['title','content','user_id'];
public function comments(){
return $this->hasMany(Comment::class);
}
public function user(){
return $this->belongsTo(User::class);
}
public function addComment($comment){
Comment::create([
'comment' => $comment,
'article_id' => $this->id,
'user_id' => auth()->id()
]);
}
}
but when I do this, I'm getting this kind of error:
"SQLSTATE[HY000]: General error: 1364 Field 'user_id' doesn't have a default value (SQL: insert into `comments` (`comment`, `article_id`, `updated_at`, `created_at`) values (Test comment bla bla bla..., 1, 2017-10-16 09:27:27, 2017-10-16 09:27:27)) ◀"
It seems I can't get the user_id in that manner, so how can I pass the user id so i can insert it into my comment tables? Thanks.
You need to add user_id to $fillable array in Comment model, not in Article model to make it work. Your original code works just because you do not use mass assignment there.
Try this:
public function addComment($comment){
Comment::create([
'comment' => $comment,
'article_id' => $this->id,
'user_id' => \Auth::user()->id
]);
}
try
\Auth::id()
instead of
auth()->id()
and go to your database and and make user_id default as NULL
The reason for this kind of error is that laravel protects database fields for mass assignment when you add the following code it makes all fields fillable.
protected $guarded = [];
Another way of doing is to add user_id in comments model. Is this case it will only allow the fields mentioned in the array to be mass assigned.
protected $fillable = [
'user_id'
];

Many to Many Relationship Join Query between Two Table in Laravel 5.1

First User table have four columns: id ,first_name, last_name,password
my Address Table have four colmnns id , company_name,email , phone
Common Address_user Table:
My User Model Look Like This http://pastebin.com/ntuvJ8Lj and my Address Model Look Like This
use Illuminate\Database\Eloquent\Model;
class Address extends Model
{
protected $fillable = [
'created_by',
'company_name',
'phone',
'email', 'address'
];
public function users()
{
return $this->belongsToMany('App\Models\User');
}
public function getAddressListAttribute()
{
return $this->users->lists('id');
}
}
now I need the all of address which login user is involved or connected
1:
in User.php
Add your relation with 'address' model:
public function addresses() {
return $this->belongsToMany( Address::class, 'address_id', 'user_id' )->withTimestamps();
}
You wrote right relation in your Address.php with users therefore, I'm not going to retype it here.
and please note that you don't need to write address_id and user_id because simply eloquent will predict it if you are following the convention, which you do!
But I can tell that you are not following the convention of naming your tables, if you want Eloquent to predict and relation between your model and your table in DB then you have to make the name of your model the singular of your table name, or vice versa, table: users model: User.
The Gist:
$user = auth()->user(); // get logged in user
$addresses = $user->addresses()->get(); // get this user addresses. or use $user->addresses only
// now you may loop through it and have each of your addresses.
foreach($addresses as $address){
var_dump($address->company_name); // or perhabs ->email
}

Resources