I am trying to develop a Restful API with Laravel 5 and i have a table as below
Schema::create('reports', function (Blueprint $table) {
$table->increments('id');
$table->string('company_name');
$table->string('direction');
$table->integer('user_id')->unsigned();
$table->timestamps();
});
The field user_id is a foreign key and i want to create a new Report record when make a POST request via Android App.
My question is, what should be the status of user_id field ? Guarded or fillable ?
Any help would be appreciated
you should set it to fillable, because you want to set this value on model creation.
For more details see the documentation:
https://laravel.com/docs/5.0/eloquent#mass-assignment
Honestly, I'd suggest to rethink that strategy, however, here's something you can try if you really need to do it that way.
Don't add user_id's to either $fillable or $guarded. Set up a model observer and assign the id right there.
That way you can pass the call safely to the Report model and still be able to hook into the process of creating/reading the Report/User
Related
So, as the title says, I have two pivot tables in my Laravel project (I am still new to Laravel). My project is an Instagram clone and I have made the like function, which uses one of the two pivot tables I made and now I'm trying to make a "Save" function, similar to the actual Instagram app where people can save posts and view them at a later time. The problem is, when I check my tinker and do:
As you can see, it looks like the likes and saves are using the pivot table for likes, I have not inserted anything into the pivot table for saves yet. Here are my migrations
For likes:
Schema::create('post_user', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->unsignedBigInteger('post_id');
$table->timestamps();
});
And for saves:
Schema::create('post_user_saves', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->unsignedBigInteger('post_id');
$table->timestamps();
});
I'm aware that they are literally the same but, just like the Instagram app, a user can like a post without saving it and vice-versa and they can also like and save it at the same time. I want to insert into the post_user_saves when I click a button. Here are pics of my database as well, as you can see the post_user_saves table is empty while post_user is not, meaning that tinker is getting the pivot data from post_user
and lastly, here are the codes from my model:
public function likes()
{
return $this->belongsToMany(Post::class);
}
public function saves()
{
return $this->belongsToMany(Post::class);
}
EDIT:
here is my store method for the likes:
public function store(Post $post){
return auth()->user()->likes()->toggle($post->id);
}
I don't have the store method for my saves yet but my plan is to make it the same as the one above hence why I need to be able to store to post_user_saves.
If your relationships are correct you can use attach() to save to the pivot:
auth()->user()->likes()->attach(auth::id(), ['column' => 'value']);
The reason your save is updating the same table is because you are pretty much using the exact same relationship so eloquent doesn't know about your post_user_saves table what you would need to do is Create a model Save() then rename your migration to save_users and update your relationship accordingly.
I installed auth in laravel 8 but I don't want to use the default table dedicated for authentication Users because I have my table Users_inf in this case I can't use migration, Users_inf also has 200 records.
Users_inf(user_id, password, user_name,privilege,active,CREATED_AT,UPDATED_AT).
CREATED_AT and UPDATED_AT added by me to be compatible with laravel.
I am using user_name for login not email. please, any suggestion for that issue.
The way I see it, you have two choices. Either alter the users table to have those fields, then import the data from one table to another - or, update your User model to use that table. In any case, you have to instruct Laravel to use the user_name field for authentication. Personally I would recommend going the first route, as that's the "Laravel way".
Approach 1: Altering the users table
Alter the migration of the users table, something along the lines of this. I would recommend you keep the ID field id rather than specifying your own user_id, as this is - again - the Laravey way of doing things.
Schema::create('users', function (Blueprint $table) {
$table->id('user_id'); // $table->id();
$table->string('user_name')->unique();
$table->string('privilege')->nullable();
$table->string('password');
$table->boolean("active")->default(true);
$table->rememberToken();
$table->timestamps();
});
Then to move the data over, run the following SQL query
INSERT INTO users (user_id, user_name, privilege, active, password, created_at, updated_at)
SELECT user_id, user_name, privilege, active, password, created_at, updated_at
FROM users_inf AS ui
WHERE NOT EXISTS(SELECT 1
FROM users AS u
WHERE u.user_name = ui.user_name)
Now you just need to instruct Laravel to use the user_name field, which we cover at the bottom of this answer.
Approach 2: Altering the User model
Laravel lets you specify which table and which field is the primary key, by adding simple properties to the model. Inside your User model class, add the following two lines,
protected $table = 'users_inf';
protected $primaryKey = 'user_id';
For both approaches: Using the user_name field as the login
Simply add the username() method to the User model class that returns the field which Laravel should use to lookup users in the given table.
public function username()
{
return 'user_name';
}
Laravel will give you so much "for free" and a lot of features will work more seamlessly when using the proper naming conventions. I suggest you follow the Laravel standards and naming conventions, as it will make it easier to work with the framework as a whole.
I have two tables like:
User:
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('loginid')->unique();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
IP:
Schema::create('i_p_s', function (Blueprint $table) {
$table->id();
$table->string('address')->unique();
$table->foreignId('user_id')->nullable();
$table->string('hostname');
$table->string('description')->nullable();
$table->timestamps();
$table->index('user_id');
});
IP Model:
public function User() {
return $this->belongsTo(User::class);
}
User Model:
public function IPs() {
return $this->hasMany(IP::class);
}
The user_id column means this IP is using by which user.
And now I want to add a new column last_modified which means who is the last editor of this row.
So I think the last_modified should be $table->foreignId('user_id')->nullable(); too.
But how to define the relationship in IP model?
Additionally, I call the user_id like this now.
$ips = IP::with('user')->get();
#foreach ($ips as $ip)
{{ $ip->user }}
#endforeach
So how can I call the last_modified after the definition?
Thanks a lot
As shown in the docs (https://laravel.com/docs/7.x/migrations#foreign-key-constraints),
$table->foreignId('user_id')->nullable();
is just a shortcut of the "old" way
Schema::table('i_p_s', function (Blueprint $table) {
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users');
});
The problem with your code would be, that you need also the constrained()-method. It will dissolve a given column name like user_id into like "Laravel, please use the column id of the table users here".
I'm not sure if the nullable()-method will be useable for this shortcut.
In the same way, your relations will be dissolved within your models. If you're not adding additional values to the belongsTo() and haveMany()-methods, Laravel will try to find its way through your databse by assuming standard naming conventions for primary keys and table names (if the table names are not set within your model).
primary keys are assumed as id. This is also the reason why $table->ip() works.
table names are assumed as the plural of the model name. That means also, you have to make sure to set the name of your i_p_s table within your IP-model as it does not follow the convention. Event better would be to think about an adaption to the convention and call your table ips.
foreign keys should be (to be able to dissolve things that way) named by the singular table name, underscore, primary key. in other words user_id.
So, your assumption should be right apart from the fact that you cannot add a second column called user_id. For your second foreign key, your code should look like the "normal/ traditional" way of doing this.
Schema::table('i_p_s', function (Blueprint $table) {
$table->unsignedBigInteger('last_modified')->nullable();
$table->foreign('last_modified')->references('id')->on('users');
});
I'm pretty sure that this will work, although I didn't tested this yet. Unfortunately, I'm not sure if you can provide the column name and table also within the constrained method. If so, that would be pretty handy. Give it a try, otherwise use the traditional way.
The relation within the model should then look like this:
public function hasChanged() {
$this->hasMany(IP::class, 'last_modified', 'id');
}
last_modified is the foreign key within the i_p_s-table while id is the local column of your owning User-model.
Bringing this into reverse for the IP-model:
public function wasChangedBy() {
$this->belongsTo(User::class, 'last_modified', 'id');
}
In both cases you can dispense on setting the 'id' column as primary key of your users-table as it is standard.
The relations are the same as in your example because of the construction/ architecture. In 99% this is always a one-to-many relation.
Last but not least, it was a bit strange to see this construction of the same foreign key two times referencing in the same table. But I found this post, which says it is eventually totally normal to do so.
Mysql: using two foreign keys to the same table
The only other way I could think of would be to have an intermediate table between i_p_s and users but this would lead to a loop in your database between these two tables which also is a thing you should avoid. So I would say this is ok.
Hope this helps ;)
I am working on an application in Laravel where users can be matched up with one users. I am trying to figure out how to setup the hasOne relationship between users.
Here is how I have done it so far. Users when they are created have a match_id which represents the user that they are matched to. When they are matched this record gets updated. Here is my table:
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('event_id');
$table->foreign('event_id')->references('id')->on('events')->onDelete('cascade');
$table->unsignedBigInteger('match_id')->nullable();
$table->foreign('match_id')->references('id')->on('users')->onDelete('cascade');
$table->string('name');
$table->string('email');
$table->timestamps();
});
In the user model I have set up this relationship:
public function match() {
return $this->hasOne(User::class);
}
but as of right now I cannot do something like this and get the expected matched user's information:
$user->match()->get();
After reading the docs here, I see that in such situations you can setup the foreign key and local key. Nevertheless I am having a difficult time figuring this out.
Could someone give me some guidance on how I could do this? I am also open to any suggestions on different implementations.
-----edit------
after discussing with some people in the comments I made some progress in my endeavors. I am able to get some results and have experimented by logging the matches. For whatever reason though I am getting the wrong matches. Look below for more information:
in my controller I log the users name and match after it has been formed:
Log::error($user->match);
Log::error('has matched with:');
Log::error($user);
Log::error('participant:');
in the database here are the users and their matches:
yet in the logs, the match is not the same:
Since the match_id is on the users table, the relationship should be belongsTo, not hasOne.
belongsTo is specified on the model that contains the foreign key.
Also, you should specify the foreign key:
return $this->belongsTo('App\User', 'match_id');
Im making a site where users can post and read each others posts.
I want to make it possible for all users to mark the posts they have read, thus showing only posts which are not read. It should also be possible to undo this. So I have a model and table for both Users and Posts.
The way I thought of doing it was creating a PostsRead table which would contain the unique id for the user and for the post. Then I could fetch all the posts and remove the ones from the PostsRead table. So the question(s) is:
1) Is this a good (best practice-ish) way to do it?
2) How? I guess I need to check if there is a record in the PostsRead-table containing the user_id/post_id pair. If it is not, insert a new row. And if it is, I guess I need to update the current row. It should maybe have a boolean field telling if the post is read or not. How would the code for this be?
When I go through it in my head, this approach seems....weird. Am I on the wrong track here? I hope the question was understandable.
Yes, it's the best practice to do it this way.
Create a table called 'user_read' with 'user_id' and 'post_id' (both as primary key)
Schema::create('user_read', function (Blueprint $table) {
$table->integer('user_id')->unsigned()->index();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->integer('post_id')->unsigned()->index();
$table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
$table->timestamps();
$table->primary(['user_id', 'post_id']);
});
To set it up you should have this relation in your model.
User.php
public function reads()
{
return $this->belongsToMany(Post::class, 'user_read', 'user_id', 'post_id')
->withTimestamps();
}
To store a new post that the user read you can just do
//1 and 3 are the posts ids
$user->reads()->sync([1, 3]);