I have the following tables, users, roles and role_user which creates the many-to-many relationship.
In my UserFactory when creating users I assign them the basic role of 'user' like so:
$factory->afterCreating(User::class, function ($user, $faker){
$roles = Role::where('name', 'user')->get();
$user->roles()->sync($roles->pluck('id')->toArray());
});
And then in my unit test I can test for users that they are redirected with a 302 when trying to access admin pages:
$this->actingAs(factory(\App\User::class)->make());
$request = Request::create('/admin', 'GET');
$middleware = new AccessAdmin;
$response = $middleware->handle($request, function () {});
$this->assertEquals($response->getStatusCode(), 302);
This works as expected but the problem is testing the reverse of this with an admin user as I'm unsure how to mock the relational data. I have tried creating the user and then attaching the roles like so:
$user = factory(\App\User::class)->make();
$roles = Role::where('name', 'admin')->get();
$user->roles()->sync($roles->pluck('id')->toArray());
This gives me the following error:
PDOException: SQLSTATE[23000]: Integrity constraint violation: 1048
Column 'user_id' cannot be null
This is correct because the user is never created in the DB so the user has no ID.
How do I mock the user/role relationship?
Are you sure you want to mock the relationship?
Instead, I would recommend recommend using DatabaseTransactions trait in your Test file, and create() method instead of make() for your factory.
use DatabaseTransactions;
....
$user = factory(\App\User::class)->create();
Related
Laravel 7.x
I need to get posts from two relations. Look:
User has Posts;
User has Friends (accessor);
Friends has Posts;
How can I get all own (User) posts and all posts by each Friend, and paginated?
Which the best way to do that?
Only to pass the idea that I want to say:
$user = User::find(1);
$posts = $user->with('posts','friends.posts')->paginate(15); // I wanna get only the Posts collection
I would suggest query your Post model and then apply filter for user and related friends filter.
$user = User::with('friends')->find(1);
$userIds = $user->friends->pluck('id')->toArray();
array_push($userIds, $user->id);
$posts = Post::whereIn('user_id', $userIds)->paginate(15);
You can use hasManyThrough, this relationship is a bit complicated to understand a provide shortcut way to access data of another mode relation.
In your User model make relationship like this way :
public function posts()
{
return $this->hasManyThrough(
Post::class,
Friend::class,
'user_id', // Foreign key on friends table...
'friend_id', // Foreign key on posts table...
'id', // Local key on users table...
'id' // Local key on friends table...
);
}
Now you can get all posts from User model like this way :
$user = User::find(1);
$posts = $user->with('posts')->paginate(15);
I have a many-to-many relationship between the User and Subscription tables in Laravel, as follows:
In Subscription.php:
public function users()
{
return $this->belongsToMany('App\User')->withTimestamps();
}
In User.php
public function subscriptions()
{
return $this->belongsToMany('App\Subscription')->withTimestamps();
}
I create a new subscription in Cashier (using Stripe) as follows:
$user->newSubscription($planname, $planname)->create();
(Note that the product and plan names are currently the same and the user's card is on record, hence the lack of a stripe token.)
But when I run this I get an error:
SQLSTATE[HY000]: General error: 1364 Field 'user_id' doesn't have a default value (SQL: insert into subscriptions (name, stripe_id, stripe_plan, quantity, updated_at, created_at) values (projects, sub_EY1zRywZ1jUjLl, projects, 1, 2019-02-17 20:07:36, 2019-02-17 20:07:36))
I'm not sure if the relationship is causing the issue or whether it's my new subscription code. How do I fix this error?
UPDATE:
I've made the following change and I still get the same error:
$user->newSubscription($planname, $planname)->create(null,[
'user_id' => $user->id,
]);
UPDATE 2:
I've made the following change and the exact same error still occurs:
$id = Auth::id();
$user = User::find($id);
// $user = Auth::user();
Do you have the Billable trait added to your User model?
General error: 1364 Field 'user_id' doesn't have a default value.
According to this, I think you need to fill the user_id column with an id value via $id = Auth::id();
Taken from laravel documentation:
Additional User Details
If you would like to specify additional customer details, you may do so by passing them as the second argument to the create method:
$user->newSubscription('main', 'monthly')->create($stripeToken, [
'email' => $email,
]);
UPDATE
Could you try this one:
$id = Auth::id();
$user = User::find($id);
$user->newSubscription($planname, $planname)->create();
I'm sure there must be a very simple way to do this - I would like to fill the user_id field on my resource with the authenticated user's id whenever a new instance of the resource is created.
In the store() method of my Resource model I have:
public function store(Request $request)
{
$input = $request->all();
$resource = Resource::create($input);
$resource->user_id = Auth::user()->id;
return $resource;
return 'Resource added.';
}
This works through a post API route, however whenever I add a new resource instance through Nova dashboard, it does not add the Auth user id. I'm guessing this because Nova doesn't use that Resource controller that I have set out?
I would appreciate suggestions!
Working on the assumption that you have relationship method User::resources() the following should work:
return $request->user()->resources()->create($request->all());
The way you have it doesn't work because you didn't save the resource after associating user with it.
I haven't used Nova yet, but since its also laravel and most likely Eloquent ORM I can tell the following.
In these two lines you've set the user_id but you haven't persisted the change:
$resource->user_id = Auth::user()->id;
return $resource;
You should add this line to save the changes you've done:
$resource->save();
As an alternative you could add the value already into your $input array:
$input = $request->all();
$input["user_id"] = Auth::user()->id;
$resource = Resource::create($input);
I just created an ResourceObserver. Saved the Auth()->user->id with the created method.
Registered the ResourceObserver to AppServiceProvider and NovaServiceProvider.
https://laravel.com/docs/5.7/eloquent#observers
https://nova.laravel.com/docs/1.0/resources/#resource-events
In the model class, add function like this:
public function save(array $options = array())
{
$this->user_id = auth()->id();
parent::save($options);
}
Just be carefull if you tend to use save in any other scenario so you don't overwrite existing user_id.
I have table with many to many relationship.
User many to many Permission
I already define many to many relationship on both model, and create the pivot table also.
What I want is get all user which contain permission name
What I have done so far is
User::all()->permissions->contains('name', 'access.backend.admin')->get();
But it give me
Undefined property: Illuminate\Database\Eloquent\Collection::$permissions on line 1
What wrong with my code?
User::All() returns a collection not model object. You have iterate over the collection to get the model object and use ->permissions().
For exapmle:
$users = User::all();
foreach ($users as $user) {
$user->permissions->contains('name', 'access.backend.admin'); // returns boolean
}
Or you can get a single model from DB as:
$user = User::first();
$user->permissions->contains('name', 'access.backend.admin'); // returns boolean
Update 1
To get users which contain desired permission use filter() method as:
$filtered_users = $users->filter(function ($user) {
if ($user->permissions->contains('name', 'access.backend.admin')) {
return $user;
}
});
Update 2
You can also write a query which returns the desired result as:
$filtered_users = User::whereHas('permissions', function($q) {
$q->where('name', 'access.backend.admin');
})->get()
I have a similar case of questions and tags, they have many to many relationship.
So when i have to fetch all question with a particular tag then i do this
$tag = Tag::where('name','laravel')->get()->first();
I first retrieved the Tag model with name laravel.
and then retrieved all questions having tag laravel.
$questions = $tag->questions;
Similarly you can do this
$permission = Permission::where('name','access.backend.admin')->get()->first();
$users = $permission->users;
Coming from CodeIgniter's Datamapper ORM I am still trying to get my head around Laravel's Eloquent ORM.
Given the fact that I have an ACCOUNT and a USER table (simplified):
ACCOUNT
- id
- name
USER
- id
- account_id
- username
One account has many users. One user belongs to one account. So we're dealing with a one-to-many relationship. Everything is already set-up in the models.
In CodeIgniter's Datamapper I would have done the following to get the user from any given ID and at the same time check if that user is related to the current account:
$u = new User();
$u->where('username', $username);
$u->where_related_account('id', $account_id);
$u->get();
if ( ! $u->exists()) exit; // or do something...
// otherwise continue to use the "$u" user object
This syntax is very logical and easy to understand. In Eloquent I have a hard time to achieve the same with a similar easy syntax. Does anyone have any suggestions?
Very simply (ignoring the relationship between the user and the account), it could just be:
$u = User::where('username', $username)
->where('account_id', $id)
->get();
That will return you your user's details.
Otherwise, assuming that you have your User and Account classes and DB tables are set up correctly (as per the Laravel docs), you should be able to just do:
$user_exists = Account::find($account_id)
->users()
->where("username", "=", $username)
->first()
->exists;
if ($user_exists)
{
doThings();
}
If you've correctly set up your models and database tables (as #msturdy said) you should actually be able to return your user account by simply going:
$user = User::whereUsername($username)
->first(); // or User::where('username', $username)->first();
if ($user) {
$account = $user->accounts()
->whereId($account_id)
->first(); // or $user->accounts()->where('id', $account_id)->first();
}
This gives you the ability to access the user and account models
you could even extend your User model to include the following methods:
class User extends Eloquent {
...
public static function byUsername($username) {
return static::whereUsername($username)->first();
}
public function getAccount($id) {
return $this->accounts()->whereId($id)->first();
}
...
}
and then simply go
$user = User::byUsername($username);
if ($user) {
$account = $user->getAccount($account_id);
}
which might be better for you if you are using the code in multiple controllers.