Laravel - Relationships issue - php

I have 3 models:
class Site extends Model
{
public function users()
{
return $this->belongsToMany('App\Models\User');
}
public function stats()
{
return $this->hasMany('App\Models\Stat');
}
}
class User extends Model
{
public function sites()
{
return $this->belongsToMany('App\Models\Site');
}
public function stats()
{
return $this->belongsToMany('App\Models\Stat');
}
}
class Stat extends Model
{
public function users()
{
return $this->belongsToMany('App\Models\User');
}
public function sites()
{
return $this->belongsTo('App\Models\Site');
}
}
So there are :
a many to many relation between sites and users
a many to many relation between users and stats
a one to many relation between site and stats
A site have a list of stats and from this list, an user can have some stats.
I'm trying to get all sites and foreach site, count of stats for the connected user.
For the moment i tried :
//repository
function getAll($user_id = 0)
{
$with = [];
$with['users'] = function ($query) use ($user_id) {
$query->where('id', '=', $user_id);
};
return Site::with($with)->orderBy('name')->get();
}
//controller
$sites = getAll($user_id);
//view
foreach($sites as $site){
$count_stats = $site->users->first()->stats->where('site_id',$site->id)->count();
}
It works but it is not very elegant, it does a lot of sql requests and the page is slower.
Do you have a better solution ?

If Sites have many Users, and Sites have many Stats, then a User has many Stats through Site
Modify User class:
public function stats() {
return $this->hasManyThrough('App\Stat', 'App\Site', 'user_id', 'site_id');
}
Eager load:
$user = User::with('stats')->find($user_id);
$stats = $user->stats();
Also, I think your Stat should belongsTo Site, since Site hasMany Stat. You need to change a lot of the belongsToMany as well since they look incorrectly used.

Related

Cannot access Intermediate table in HasManyThrough relationship

My DB schema looks like this.
Now, in artisan tinker mode, When I try to query Details table from user Model, it shows me the records of the details table but I cannot access the the Cases Model for some reason, it always returns NULL in tinker.
This is my User Model
public function details()
{
return $this->hasManyThrough('App\Models\Detail', 'App\Models\Cases', 'user_id', 'case_id', 'id', 'id');
}
What am I doing wrong?
If for convenience you want to access Details directly from the User model then you can define relations as - (may seem like a little duplication but worth if it results in ease)
class User extends Model
{
public function cases()
{
return $this->hasMany(Case::class);
}
public function details()
{
return $this->hasManyThrough(Detail::class, Case::class);
}
}
class Case extends Model
{
public function details()
{
return $this->hasMany(Detail::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
}
class Detail extends Model
{
public function case()
{
return $this->belongsTo(Case::class);
}
}
Now both cases and details can be directly accessed via User record
$user->cases;
$user->details;
The idea of hasManyThrough is to skip the intermediate table. If you need to look at the cases and the details maybe you should define other relations for it.
// User model
public function cases()
{
return $this->hasMany(Cases::class, 'user_id');
}
// Cases model
public function details()
{
return $this->hasMany(Detail::class, 'user_id');
}
$users = User::with('cases.details')->get();
foreach ($users as $user) {
// an user
foreach ($user->cases as case) {
// a case
foreach ($case->details as $detail) {
// the details of a case
}
}
}

Laravel Eloquent elation for pivot table

I'm currently working on a laravel project, but I'm kind of stuck finding the right eloquent relations.
My tables and the connections (should) look like this:
Project Relations
My model relations look like this:
User
public function team()
{
return $this->hasMany(Team::class, 'user_id');
}
public function evaluation()
{
return $this->hasMany(Evaluation::class, 'user_id');
}
Team
public function user()
{
return $this->belongsTo(User::class);
}
public function survey()
{
return $this->hasMany(Survey::class, 'team_id');
}
Evaluation
public function user()
{
return $this->belongsTo(User::class);
}
public function survey()
{
return $this->hasMany(Survey::class, 'evaluation_id');
}
Survey
public function team()
{
return $this->belongsTo(Team::class);
}
public function evaluation()
{
return $this->belongsTo(Evaluation::class);
}
public function surveyresponse()
{
return $this->hasMany(SurveyResponse::class, 'survey_id');
}
SurveyResponse
public function survey()
{
return $this->belongsTo(Survey::class);
}
public function testquestion()
{
return $this->belongsTo('App\TestQuestion');
}
Is this the way to go? Do I need a "Has Many Through" relation here? Or a "Polymorphic Relationship"?
Seems correct to me, i just didnt see the TesteQuestion model (your last relation).
Answering your question:
The HasManyThrough relation is just a shortcut for accessing distant relations via an intermediate relation, in your case: Users has many evaluations that has many surveys. With this relationship you could get all surveys from a user.
Your relation would look like this:
/**
* Get all of the surveys for the user.
*/
public function surveys()
{
return $this->hasManyThrough('App\Survey', 'App\Evaluation');
}
You can access this relation like this:
$user->surveys();
But you can achieve the same (without using the HasManyThrough) by doing:
$user->evaluations()->surveys();
Beware that this will return the evaluations too, not just the surveys and it requires more processing.
So i recommend you doing the HasManyThrough relationship if you pretend to access the surveys a lot.

How to join three tables with Laravel and keep data integrity

I am using Laravel 5.7 and now I am trying setup a relationship between three tables named:
Tickets (PK - TicketID, FK - CampusID)
Campus (PK - CampusID, FK - TechID)
User (PK - TechID)
I don't think I set up my models correctly as I am showing a ticket where the CampusID doesn't belong to the TechID. I am looking for a best practice on setting up Eloquent to keep the data integrity in place so I can prevent any abnormalities. As mentioned above the foreign key for Tickets should reference the Campus primary key, and Campus foreign key should reference the User primary key.
Here are my Models:
Ticket
protected $table='tickets';
public function user() {
return $this->belongsTo(User::class);
}
Campus
protected $table='campus';
public function user() {
return $this->belongsTo(User::class);
}
User
public function campus()
{
return $this->hasMany(Campus::class, 'TechID');
}
public function ticket()
{
return $this->hasMany(Ticket::class, 'AssignedTo');
}
Here is my controller:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Auth;
use App\Campus;
use App\Ticket;
class PagesController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
}
// Dashboard Page
public function index()
{
$user = Auth::user();
$campuses = Campus::where('TechID',$user->id)->pluck('CampusName');
$tickets = Ticket::all()->where('AssignedTo', $user->id);
return view('home')->with(['user' => $user,'campuses'=>$campuses,'tickets'=>$tickets]);
}
// Queue Page
public function Queue() {
return view('Pages.Queue');
}
// Reports Page
public function Reports() {
return view('Pages.Reports');
}
// Search Page
public function Search() {
return view('Pages.Search');
}
}
I think my models are fine, but my controller is probably where I made some mistakes. I've tried reading questions on here already, watching videos, and reading the Laravel docs, but nothing has really clicked with me yet. I really appreciate any and all help. Ideally it should cascade changes. So if I have a situation where I want to change what location a tech belongs to I could just make the change in the Campus table probably in the TechID column.
I would use Eager Loading.
public function index()
{
$user = User::with([
'campuses' => function($query) {
$query->select(['id', 'CampusName']);
},
'tickets'
])->where('id', Auth::id())->first();
$campuses = $user->campuses->pluck('CampusName');
$tickets = Ticket::all()->where('AssignedTo', $user->id);
return view('home')->with([
'user' => $user,
'campuses'=>$user->campuses->pluck('CampusName'),
'tickets'=>$user->tickets]);
}
EDIT
You need to update your User model.
public function campuses()
{
return $this->hasMany(Campus::class, 'TechID');
}
public function tickets()
{
return $this->hasMany(Ticket::class, 'AssignedTo');
}

Eloquent many-to-many-to-many - how to load distant relation easily

I have 3 tables; users, groups and permissions
In models I have the relationships set as belongsToMany
in user model:
public function groups() {
return $this->belongsToMany('Group');
}
in group model:
public function users() {
return $this->belongsToMany('User');
}
public function permissions() {
return $this->belongsToMany('Permission');
}
in permissions model:
public function groups() {
return $this->belongsToMany('Group', 'id');
}
many users - to - many groups
many groups - to - many permissions
I'm trying to get all the permissions a user has, and have no clue what the code for it should look like. Can anyone help?
This is how you can do it:
User::where('id', $id)->with(['groups.permissions' => function ($q) use (&$permissions) {
$permissions = $q->get()->unique();
}])->first();
// then
$permissions; // collection of unique permissions of the user with id = $id
It should look something like this if you are eager loading...
$user = User::where('id', $id)->with(['groups.permissions'])->first();

Laravel 4 eager loading

I have a User model, a Recipe model, and a Cookbook model.
A User can 'own' many recipes. (User -OneToMany- Recipe)
A user has 'only one' Cookbook. (User -OneToOne- Cookbook)
Recipes that a user owns belong to his Cookbook.
One Recipe can belong to many Cookbook. (Recipe sharing) (Cookbook -ManyToMany- Recipe)
Many Users may LIKE Many Recipe. (User -ManyToMany- Recipe)
Many Users can follow Many Cookbook. (User -ManyToMany- Cookbook)
One User can have many friends who are Users (User -OneToMany- User)
What I want to do is load recipes for the currently logged in user.
I want to load the recipes owned by his friends and the recipes of all the cookbooks the logged in user is following.
class User extends Eloquent {
public function friends() {
return $this->belongsToMany('User', 'user_friends', 'user_id', 'friend_id');
}
public function cookbook() {
return $this->hasOne('Cookbook', 'owner_id', 'id');
}
public function recipes() {
return $this->hasMany('Recipe', 'owner_id', 'id');
}
public function followedCookbooks() {
return $this->belongsToMany('Cookbook','cb_followers', 'follower_id', 'cb_id');
}
}
class Recipe extends Eloquent() {
public function owner() {
return $this->belongsTo('User', 'owner_id', 'id');
}
public function cookbooks() {
return $this->belongsToMany('Cookbook', 'cb_recipes', 'recipe_id', 'cb_id');
}
}
class Cookbook extends Eloquent() {
public function owner() {
return $this->belongsTo('User', 'owner_id', 'id');
}
public function recipes() {
return $this->belongsToMany('Recipe', 'cookbooks_recipes', 'cookbook_id', 'recipe_id');
}
public function followers() {
return $this->belongsToMany('User', 'cb_followers', 'cb_id', 'follower_id');
}
}
What I did is this:
$user = Auth::user();
$friends = $user->friends();
$cookbooks = $user->followedCookbooks();
$recipes = array();
foreach($friends as $friend) {
$recipe = $friend->recipes();
array_push($recipes, $recipe);
}
foreach($cookbooks as $cookbook) {
$cbRecipes = $cookbook->recipes()
foreach($cbRecipes as $cbRecipe) {
array_push($recipes, $cbRecipe);
}
}
But this method would run a lot of SQL queries. How can I use eager loading to reduce the number of queries?
How about
$user = Auth::user();
$recipesFriends = $user->friends()->with('friends.recipes')->get();
$recipesCookbooks = $user->with('followedCookbooks')->with('followedCookbooks.recipes')->get();
If you want to go a bit further you can take a look at this answer: How to list out all items in a nested table in Laravel
You may try this:
$userData = Auth::user()->load('cookbook.recipes')
->load('friends.cookbook.recipes');
If you want to load friends recipes in a different variable then you may try this:
$userData = Auth::user()->load('cookbook.recipes')
->load('friends.cookbook.recipes');
$friendsData = $userData->friends;

Categories