I have default user model. Now I have another table for Administrators. Is there a way to return user fields (since admin is extending user) from admin model ? I found this example but its when admin_id is present in user model. I have one-to-one relation here.
class Admin extends User
{
protected $table = 'users';
public static function boot()
{
parent::boot();
static::addGlobalScope(function ($query) {
$query->where('is_admin', true);
});
}
}
This is the example I found. I'm not sure how can I return user fields from my admin model when its on different table.
The point is I want to be able to do something like this (call methods from users):
Admin::first()->posts()
Where posts method is not on Admin class but on user class.
Edit:
To explain it better. I have two tables, users and admins. They are connected in one-to-one relationship so I can do something like this:
$admin = Admin::first();
$posts = $admin->user()->posts();
but since Admin should have all fields from users table and one more field from admins table I'm looking for a way to do this:
$admin = Admin::first();
$posts = $admin->posts();
I don't want to add admin or something to users table, I still want to use admins table since I will need more fields there later.
If both tables have an equal id, use a trait to define your relationships:
trait UserRelationships {
public function posts() {
return $this->hasMany(Post::class, 'user_id');
}
}
class Admin {
use UserRelationships;
}
class User {
use UserRelationships;
}
You'll just have to be sure to explicitly declare the foreign key name in the relationship.
You could also extend the User model and override the $table property but this may present problems for various reasons since User properties exist on the user relationship and not on the Admin model.
Related
I have 2 models: User and Role.
A user can have many roles.
A role can have many users.
I have a custom pivot model between these 2 models. This custom pivot model only exists because it uses a trait that listens/logs for events such as created, updated, deleted.
Let's say I have a role called moderator. When I attach() (or detach()) that role to 5 users, it does successfully fire 5 created (or deleted) events for the pivot table.
$roleModerator->users()->attach($anArrayOfFiveUsersHere);
So far so good.
My problem is the following: when I delete the moderator role itself, it does delete all pivot rows associated to the role, but it does not fire any deleted event for each deleted pivot rows.
Expected behavior: I want Laravel to fire deleted events for each rows it deletes in the pivot table when I ask it to delete the role.
Environment: PHP 7.3 / Laravel 6
One weird thing I noticed, if I add this to my Role model :
public static function boot()
{
parent::boot();
static::deleting(function (self $model)
{
//$model->users()->detach(); // <-- this fails firing deleted events.
//MyCustomPivot::query()->where('role_id', $model->id)->get()->each->delete(); // <-- this fails firing deleted events.
$model->users()->sync([]); // <--- this works!
});
}
sync([]) will work great and fire as many deleted events as it deletes pivot rows.
but detach(), although it accomplishes the same thing, won't fire any deleted event. Why is that? They are both from InteractisWithPivotTable.php and sync() does even call detach() itself!
Not 100% sure it's applicable to your situation, but according to this issue on Github, you need to do some setup in your models.
First, make sure you have a primary key column in your table and defined on your pivot model.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\Pivot;
class MyCustomPivot extends Pivot
{
public $primaryKey = "id";
public $incrementing = true;
}
Second, make sure you include your custom pivot model in your relationships.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
public function users()
{
return $this->belongsToMany(User::class)
->using(MyCustomPivot::class)
->withPivot('id');
}
}
class User extends Model
{
public function roles()
{
return $this->belongsToMany(Role::class)
->using(MyCustomPivot::class)
->withPivot('id');
}
}
I know that hasOneThrough has been introduced to the latest Laravel build, however I can't really upgrade right now. What is the best way to do this?
I have got a users table, which I can't change the structure off, but I need to assign a role to these users, so I have created a pivot table, I want to add a method to my User model to get the role, and the user can only have ONE.
Tables:
users user_roles user_assigned_roles
I could use hasManyThrough, but this would expect many and I want to return a single model rather than a collection .
You could manage this with a combination of one-to-one & one-to-many relationships, and then access the role through an accessor:
`users` 1 --- 1 `user_assigned_roles` m ---- 1 `user_roles`
So, in a UserAssignedRole model:
/** UserAssignedRole.php */
public function role()
{
return $this->belongsTo('App\UserRole');
}
Then in your User model:
/** User.php */
public function assigned_role()
{
return $this->hasOne('App\UserAssignedRole');
}
// defining an accessor for your role:
public function getRoleAttribute()
{
return $this->assigned_role->role; // <--- Access the role of 'UserAssignedRole'
}
So in your controller (or wherever you want) you could do:
/** UsersController.php */
public function myFunction()
{
$user = User::find(1);
$role = $user->role; // <--
dd($role->name);
//
}
PS1: I strongly suggest you to upgrade to the latest version of Laravel to make use of the new features and also for security reasons, fixes etc.
PS2: There is package called Eloquent Has Many Deep by Jonas Staudenmeir that manages this kind of relationship (and more) for you.
I have a question about models structure.
I created a model called User.php
Than I would like after get a record from DB initialize another class which extends User class based on the value from DB. I.e. there is a record from DB users
id = 1
name = John
type = 1
If type = 1 I would like to init some other class, i.g. Admin
And folders structure will be
Models
- User.php
- UserTypes
- Admin.php
How it's possible to realize this?
Thanks
You can achieve that by using for polymorphic relation. Let's say a user can be extended by two models "Admin" and "Marketer", they will look something like that:
class Admin extends Model
{
public function user() {
return $this->morphOne('App\User', 'extendable');
}
}
And the User model:
class User extends Model
{
public function extendable() {
return $this->morphTo();
}
}
Of course you will also need to add two columns to your User model extendable_id and extendable_type to hold the relation.
To read more you can check laravel documentation https://laravel.com/docs/5.5/eloquent-relationships#polymorphic-relations
Can any body tell me what is the main difference between
the BelongsTo and HasOne relationship in eloquent.
The main difference is which side of the relation holds relationship's foreign key. The model that calls $this->belongsTo() is the owned model in one-to-one and many-to-one relationships and holds the key of the owning model.
Example one-to-one relationship:
class User extends Model {
public function car() {
// user has at maximum one car,
// so $user->car will return a single model
return $this->hasOne('Car');
}
}
class Car extends Model {
public function owner() {
// cars table has owner_id field that stores id of related user model
return $this->belongsTo('User');
}
}
Example one-to-many relationship:
class User extends Model {
public function phoneNumbers() {
// user can have multiple phone numbers,
// so $user->phoneNumbers will return a collection of models
return $this->hasMany('PhoneNumber');
}
}
class PhoneNumber extends Model {
public function owner() {
// phone_numbers table has owner_id field that stores id of related user model
return $this->belongsTo('User');
}
}
BelongsTo is a inverse of HasOne.
We can define the inverse of a hasOne relationship using the belongsTo method.
Take simple example with User and Phone models.
I'm giving hasOne relation from User to Phone.
class User extends Model
{
/**
* Get the phone record associated with the user.
*/
public function phone()
{
return $this->hasOne('App\Phone');
}
}
Using this relation, I'm able to get Phone model data using User model.
But it is not possible with Inverse process using HasOne. Like Access User model using Phone model.
If I want to access User model using Phone, then it is necessary to add BelongsTo in Phone model.
class Phone extends Model
{
/**
* Get the user that owns the phone.
*/
public function user()
{
return $this->belongsTo('App\User');
}
}
You can refer this link for more detail.
One-to-one relationship: You, as a User, can have one (hasOne) Profile. And of course the inverse also applies. Profile (belongsTo) a User. A user can't have more than one profile and a profile can't belong to multiple users.
If you want to make One TO one relationship between two table then first you have to make "hasOne" Relation and If you want to make inversely table relationship then you make " "Belongs to"... IT is a simple difference between HasOne and Belongs to the relationship if you want to know about this
One To Many (Inverse)
Now that we can access all of a post's comments, let's define a relationship to allow a comment to access its parent post. To define the inverse of a hasMany relationship, define a relationship function on the child model which calls the belongsTo method:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
/**
* Get the post that owns the comment.
*/
public function post()
{
return $this->belongsTo('App\Post');
}
}
Here you can see a good example and see what the difference is between BelongsTo and HasOne relationship in eloquent.
Eloquent Relationships Cheat Sheet by Mahmoud Zalt https://link.medium.com/9lj9BAG8lR
Okay so I'm building a pretty large application in Laravel. Users manage their own virtual soccer teams. I have a users table then I have a teams table with team specefic things like name, level,and arena, etc. For the arena though I decided to add a arenas table and then add a arena_id column in the teams table instead of just adding the arena name to the teams table.
so here is the basic relantionship:
User hasOne Team
Team hasOne User
Team hasOne Arena
Arena hasOne Team
so if I wanted to get the arena for a user I call the method
$user = User::with('team')->where('username', '=', $username)->first();
$user->team->arena->arena_name;
and everything works fine; however I feel there is a much cleaner or simpler way of doing this. Is there or is this fine for the aplication?
There is nothing wrong with the way you are doing it. That is a perfectly good way of doing it for your needs. However something that might help is creating a getArenaFromUsername() method in the User model. Your User model would look something like this:
<?php
class User extends \Eloquent {
protected $fillable = [];
public function getArenaFromUsername($username)
{
$user = User::with('team')->where('username', '=', $username)->first();
return $user->team->arena->arena_name;
}
}
So then to get the arena name from a controller you just do:
$user = new User;
$arena = $user->getArenaFromUsername($username);
-----------------------------------------OR-----------------------------------------------
Or use dependency injection by doing the following in your controller using the same method we just created in the model:
protected $user;
public function __construct(User $user)
{
$this->user = $user;
}
then to use it you can use one line in any method in your controller like so:
$this->user->getArenaFromUsername($username);
These are all different ways of abstracting your query to make it more reusable and cleaner to call in your controller. Don't be afraid to make public methods in your model to call.
A couple things.
You can eager load the sub-relationship like so:
User::with('team', 'team.arena')...
You can also create an accessor function (http://laravel.com/docs/eloquent#accessors-and-mutators) on your User model to make it a first-class property on the User object:
// accessed via $user->arena
public function getArenaAttribute {
return $this->team->arena;
}
// accessed via $user->arenaName
public function getArenaNameAttribute {
return $this->team->arena->arena_name;
}