laravel one-to-many relationship is null - laravel

I have 3 models: User, Company and Branch.
In a blade file i want to be able to display the branch the company of the user belongs to.
In my opinion i should have the following relationships:
User -> Company : user belongsto a company and a company has many users , so this in a one-to-many relationship.
Company -> Branch: A company belongsto a branch and a branch can have many companies. So once again a one-to-many relationship.
I have foreign keys in the users table: company_id which references id on the company table.
Another FK in the company table: branch_id which references id on the branch table.
In my blade file want to display the branch name like this: {{ $user->company->branch->name }} where only name is a property. Company and branch are relationships.
My query looks like this:
$users = User::with(['company','company.branche' => function($q){
$q->select('name');
}])->inRandomOrder()->paginate(24);
<?php
namespace App;
use App\Events\GeneralEvent;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Nicolaslopezj\Searchable\SearchableTrait;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable implements MustVerifyEmail
{
use Notifiable, SoftDeletes, HasRoles, SearchableTrait;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'gender', 'first_name','last_name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
...
public function company()
{
return $this->belongsTo(Company::class,'company_id');
}
}
<?php
namespace App;
use App\Events\GeneralEvent;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Nicolaslopezj\Searchable\SearchableTrait;
class Company extends Model
{
use SoftDeletes, SearchableTrait;
...
public function branche()
{
return $this->belongsTo(Branche::class);
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Nicolaslopezj\Searchable\SearchableTrait;
class Branche extends Model
{
protected $fillable = [
'name'
];
protected $searchable = [
'columns' => [
'name' => 10,
],
];
public function companies()
{
return $this->hasMany(Company::class);
}
}
However when i dump the $user->company i get null. So adding the branch after that is pointless for now. When i dump the user the relation shows up but is null. I have no idea where i am going wrong. Can someone please help?

You have to include the id of the branch so that eloquent can link the branch and company
you can do like that
$users = User::with(['company','company.branche' => function($q){
$q->select('id','name');
}])->inRandomOrder()->paginate(24);
OR Like that
$users = User::with('company.branche:id,name')->inRandomOrder()->paginate(24);

So the problem was i had one of my foreign keys pointing to the wrong table. Doh.

Related

Loading a belongs to relationship inside of a laravel resource collection

I'm having some issues loading my relationships into a ResourceCollection to be consumed by an API, I want to load blogs that each belong to a category.
The blog model which uses a belongsTo relationship
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
class BlogPost extends Model {
use HasFactory, SoftDeletes;
protected $fillable = [
'title',
'content',
'seo_title',
'seo_content',
];
public function categories(): BelongsTo {
return $this->belongsTo(BlogCategory::class);
}
}
The Category model has a hasMany to blogs
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class BlogCategory extends Model {
use HasFactory;
protected $fillable = [
'slug'
];
public function blogs(): HasMany {
return $this->hasMany(BlogPost::class);
}
}
Inside of the blog_post migration, I added a foreign key to blog_categories
$table->foreignId('category_id')->constrained('blog_categories');
Then, in my BlogPost ResourceCollection I tried loading the relationship,
#[ArrayShape(['data' => "\Illuminate\Support\Collection", 'category' => AnonymousResourceCollection::class])] public function toArray($request): array {
return [
'data' => $this->collection,
'category' => BlogCategoryCollection::make($this->whenLoaded($this->categories))
];
}
I call the collection inside of the index function of my controller
public function index(): BlogPostCollection
{
return new BlogPostCollection(BlogPost::all());
}
And when I hit the api/blogs endpoint I get the error :
Property [categories] does not exist on this collection instance.
Managed to fix it in the end.
Changed the BlogPostResourceCollection to the following
return [
'data' => $this->collection,
'categories' => BlogCategoryCollection::collection($this->whenLoaded('categories'))
];
seems to work in the end.

Laravel Auditing package working only on User model and not working on other models

If I try to put on another model only new created record is auditing. Update, delete is not working.
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;
use Illuminate\Database\Eloquent\Model;
use OwenIt\Auditing\Contracts\Auditable;
class Brands extends Model implements Auditable
{
use \OwenIt\Auditing\Auditable;
use Notifiable;
use HasRoles;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'brand_name', 'brand_status'
];
protected $auditInclude = [
'brand_name', 'brand_status'
];
}
I don't know why in User model it is tracking all events. But in another model, only the newly created record is tracking and not other things like deleting, updating it is not tracking.
Inside your config/audit.php , make sure that the timestamps setting is set to true. This allows the created_at, updated_at and deleted_at timestamps to be audited.
'timestamps' => true,
EDIT: Add created_at, updated_at and deleted_at inside your protected $auditInclude fuunction
protected $auditInclude = [
'brand_name', 'brand_status', 'created_at', 'updated_at', 'deleted_at'
];

ORM not working in Default UserController & User Model in laravel

I want to get User wise Role. here is I'm facing error ....
UserController.php ( user controller file )
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests\UserRequest;
use App\Employee;
use App\Role;
use App\User;
use App\Site;
use App\Client;
use App\ProjectType;
use App\UserPermission;
use Auth;
use DB;
use App\Project;
class UsersController extends BaseController {
public function __construct() {
$this->isSetClientAndProjectType();
$data = User::with('Role')->first();
echo "<pre>";print_r(json_decode($data)); die;
}
}
User.php ( user model file )
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Database\Eloquent\SoftDeletes;
class User extends Authenticatable {
use SoftDeletes;
use Notifiable;
protected $fillable = [
'name', 'role_id', 'password', 'siteid', 'email', 'status', 'allowed_to_bypass_pm', 'allowed_to_bypass_admin'
];
protected $hidden = [
'password', 'remember_token',
];
// Get users roles
public function Role() {
return $this->hasMany('App\Role', 'role_id', 'id');
}
}
Error is
How can i solve this error?
Help me guys.
Thank You.
If a user has many "roles" it should be public function roles().
You have defined:
A single user has a role_id
Therefore you need:
If a user has a single role it would be:
public function role() {
return $this->belongsTo('App\Role');
}
The reverse on the Role model would be:
public function users() {
return $this->belongsToMany('App\User');
}
Since many users can have the same role.
Hope this helps.
You need to add belongsTo relationship
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Database\Eloquent\SoftDeletes;
class User extends Authenticatable {
use SoftDeletes, Notifiable;
protected $fillable = [
'name', 'role_id', 'password', 'siteid', 'email', 'status', 'allowed_to_bypass_pm', 'allowed_to_bypass_admin'
];
protected $hidden = [
'password', 'remember_token',
];
// Get user's role
public function role() {
return $this->belongsTo('App\Role');
}
}
Now fetch data
$user = User::with('role')->find(1);
$role = $user->role;
You need to make sure your table structure and foreign key references are compatible with the model relationship methods used.
For example, you have used "HasMany" relationship on User model. For that, you will have to make sure that each record/row in users table(User model) "has many" associated records/rows in roles table(Role model).
Here HasMany method assumes a foreign key "role_id" on roles table(Role Model). On not finding of which, it throws error.
You first need to take in consideration the table structure of roles and users table(User and Role model) as per your requirement, and than add model relationship methods accordingly.
It can be a bit tricky if you are using the methods for the first time, you can refer the laravel documentation for the same:
eloquent-relationships

How to properly setup a cross database relationship in laravel? +eloquent

I can't seem to figure out how to have a table in one database have a relationship with another table in a different database.
like Customer uses the 'directory' database.
namespace App\Models;
use App\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Customer extends Model
{
use SoftDeletes;
protected $connection = 'directory';
public $fillable = [
'id',
'customer_type_id',
'user_id'
];
public function owners(){
return $this->hasMany(User::class)->whereHas('roles', function($query){ $query->where('name', 'owner');});
}
public function employees(){
return $this->hasMany(User::class)->whereHas('roles', function($query){ $query->where('name', 'employee');});
}
}
Then in the User::class just uses the default 'mysql' database
namespace App;
use App\Models\Customer_form;
use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use HasApiTokens, Notifiable, SoftDeletes;
protected $connection = 'mysql';
protected $fillable = [
'name', 'email', 'password',
];
protected $hidden = [
'password', 'remember_token',
];
protected static function boot() {
// create an event to happen on deleting
static::deleting(function($user) {
$user->roles()->detach();
});
}
public function roles()
{
return $this
->belongsToMany("App\Role")
->withPivot('id','can_read', 'can_create', 'can_update', 'can_delete');
}
public function customer(){
return $this->belongsTo(App\Models\Customer::class, "customer_id", "id")->with("primaryName");
}
}
Then i am calling this relationship in my controller like so:
$customerUsers = Customer::has('owners')->with('owners')->with('employees')->get();
Then it gives me an error like its trying to query just one of the databases looking for a table that doesn't exist in THAT database( "directory" )
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'directory.role_user' doesn't exist (SQL: select * from `customers` where exists (select * from `users` where `customers`.`id` = `users`.`customer_id` and exists (select * from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `users`.`id` = `role_user`.`user_id` and `name` = owner)) and `customers`.`deleted_at` is null)
Any idea what i am missing? Or can you not do cross-database relations?
Check this package, it allows you to use cross database subquery (has, whereHas, doesntHave, whereDoesntHave, withCount).
https://github.com/hoyvoy/laravel-cross-database-subqueries
The solution to my issue was using making an attribute method in the model that made a call to the second database.

Specifying morphTo method target id column

Is there a way to specify target id column on morphTo method?
I'll give an example which is in documentation for laravel:
Post - Id, text
Video - Id, url
Comment - Id, text, commentable_id, commentable_type
But what if Ids on post and video were renamed to custom_id? How would I set up my eloquent model then? Thanks.
Edit:
I still don't get it here is the complete code:
Table structure:
comments - id, text, commentable_id, commentable_type, user_id
posts - custom_id, text
videos - custom_id, url
users - id, name, email, password,...
Comment model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
public function commentable()
{
return $this->morphTo();
}
}
Video model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Video extends Model
{
public function comments()
{
return $this->morphMany('App\Comment', 'commentable', 'commentable_type', 'commentable_id', 'custom_id');
}
}
Post model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function comments()
{
return $this->morphMany('App\Comment', 'commentable', 'commentable_type', 'commentable_id', 'custom_id');
}
}
User model:
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function comments()
{
return $this->hasMany('App\Comment');
}
}
TestController:
<?php
namespace App\Http\Controllers;
use App\User;
use Illuminate\Http\Request;
class TestController extends Controller
{
public function test()
{
$user = User::first();
$comments = $user -> comments;
foreach ($comments as $comment)
{
return $comment -> commentable;
}
}
}
And it still throws query exception Unknown column posts.id
Please explain. Thanks.
You can set the custom id field name in the relationship.
public function comments()
{
return $this->morphMany('App\Comment', 'commentable', 'commentable_type', 'commentable_id', 'custom_id');
}
There are the parameters
public function morphMany($related, $name, $type = null, $id = null, $localKey = null);
Edit : After going through the whole thing i've found out that using custom id only works from parent fetches the child relationship. But fails when trying to get the parent using the children. So fetching Post or Video from the comment would fail.
There's an active proposal on laravel internals to add the feature to allow specifying the custom id to make this work. https://github.com/laravel/internals/issues/587

Resources