Laravel - Check all instances of relationship for values on pivot table - php

I have a two models that have a many-to-many relationship, with some addtional fields on the pivot table:
Shift table:
------------
id - uuid
date - date
address - string
...
Employee table:
---------------
id - uuid
first_name - string
...
shift_employee table:
---------------------
id - integer
shift_id - uuid
employee_id - uuid
hours_worked - integer
...
Now, I'm making a Lens in Laravel Nova, and I want to use the query object to check if any of the instances on shift_employee related to a specific shift has a value bigger than 0 for hours_worked on the shift_employee table.
My first idea is to somehow use whereHas assuming that the Shift model has a relationship employees, like this:
$query->whereHas('employees' function ($q) {
$q->where('hours_worked', '>', 0);
});
But... this is not working... There are shifts with more than 0 hours_worked for certain employees and this query string is not working for me. How would I do this?

First make sure your models are modeled correctly. If they are, you can access any attribute of an intermediate table with the pivot attribute as below:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
/**
* The users that belong to the role.
*/
public function users()
{
return $this->belongsToMany('App\User');
}
}
exemple :
$user = App\User::find(1);
foreach ($user->roles as $role) {
echo $role->pivot->created_at;
}
In your case, try :
$employee = Employee::with('Shift');
foreach($employee as $e){
$employeeHw[] = $e->shift_employee->where('hours_worked', '>', 0)->get();
}
I'm also new to laverel, so I'm not absolutely sure it works, but in theory: P
Usually in these cases I use the query bilder with join which I find easier
$users = DB::table('users')
->join('contacts', 'users.id', '=', 'contacts.user_id')
->join('orders', 'users.id', '=', 'orders.user_id')
->select('users.*', 'contacts.phone', 'orders.price')
->get();

Related

Return data from pivot table with whereIn

So I have Status class which has pivot table relationship with roles:
public function roles():
{
return $this->belongsToMany(Role::class, 'status_role', 'status_id', 'role_id');
}
This is how Status db table looks:
id title
1 status1
2 status2
3 status3
And then my pivot table which looks like this:
status_id role_id
1 2
2 2
And now I want to write query which returns statuses with role_id=2.
Basically it should return data like this: status1, status2 and not include status3.
What I have tryed:
$statuses = Status::query()
->leftJoin('status_role', function ($join) {
$join->on('statuses.id', '=', 'status_role.status_id')
->whereIn('status_role.role_id',[2]);
})
->get();
But now it returns all statuses (status1, status2, status3) it should be only (status1 and status2). How I need to change it?
This query will return all statuses attached to roles with id 2:
Status::query()->whereHas('roles', function($q){
$q->where('id', 2);
})->get();
It uses the whereHas method that can be useful when you need to query relationships.
It can do a lot more, you should check the documentation on this topic: https://laravel.com/docs/8.x/eloquent-relationships#querying-relationship-existence
Quick note: whereHas is the "Laravel preferred way" of doing what you are trying to achieve.
However, you should be able to also do it with this query, which is closer to your current code:
$statuses = Status::query()
->join('status_role', function ($join) {
$join
->on('statuses.id', '=', 'status_role.status_id')
->where('status_role.role_id',2);
})
->get();
// I replaced the leftJoin by join, which will exclude all results without roles (e.g. status id 3)
// or even simpler:
$statuses = Status::query()
->join('status_role', 'statuses.id', '=', 'status_role.status_id')
->where('status_role.role_id',2)
->get();

Laravel - Displaying fields with many to many relationship according field in pivot table

I have this database structure
table users table office_user table offices
----------- ----------------- -------------
id * id * id *
full_name user_id name
office_id
joined_at
So in my project every office has many users and user can be joined to many offices in date (joined_at)
User.php model
public function offices()
{
return $this->belongsToMany('App\Office)->withPivot('joined_at');
}
Office.php model
public function users()
{
return $this->belongsToMany('App\User)->withPivot('joined_at');
}
OfficeController.php
public function show(Office $office)
{
$users = User::with(array('phones', 'offices' , function($query)
{
$query->orderBy('joined_at', 'desc');
}))->get();
return view('dashboard.offices.show', compact(['office', 'users']));
}
I need two things :
1- Get current users list for every office
2- Count of current users in every office
I already achieve this:
<h3>{{ $office->name }}</h3><span>{{ $office->users->count() }}</span>
#foreach ($office->users as $user)
<li>{{ $user->full_name }}</li>
#endforeach
But the result is not as expected it gives me all users in certain office and count of them regardless there joined date
I want the list of last joined users to this office and count of them according joined_at field in pivot table
Thank you and Sorry for my english
But the result is not as expected it gives me all users in certain office and count of them regardless there joined date
When you do $office->users->count() that is the expected behavior because you are retrieve all the associated users of every office at any time, so given that you returned all this users, the count() executed in the collection will count all of them.
Your pivot attribute is just a timestamp, so how would you reduce the number of users returned? users that joined the office today/in the last hour/in the last 15 min maybe?
If so, you can add constrains to your count() method to get the results you want.
As an example, in the following lines we are gonna constraint the associated offices that has a joined_at that belongs to today:
public function show(Office $office)
{
$users = User::with([
'phones',
'offices' => function ($offices) {
$offices->whereDate('joined_at', '>', now()->startOfDay());
},
])->get();
return view('dashboard.offices.show', compact([office, 'users']));
}
Check this section of the documentation:
Constraining Eager Loads
Sometimes you may wish to eager load a relationship, but also specify
additional query conditions for the eager loading query. Here's an
example:
$users = App\User::with(['posts' => function ($query) {
$query->where('title', 'like', '%first%');
}])->get();
In this example, Eloquent will only eager load posts where the post's
title column contains the word first. You may call other query
builder methods to further customize the eager loading operation:
$users = App\User::with(['posts' => function ($query) {
$query->orderBy('created_at', 'desc');
}])->get();

Error in laravel query builder

My Laravel query builder return null:
$country = DB::table('participate_company')
->join('company', 'company.company_id', '=', 'participate_company.company_id')
->join('country', 'country.country_id', '=', 'company', 'company.country_id')
->join('competition', 'competition.competition_id', '=', 'participate_company.competition_id')
->select('country.country_name', DB::raw('COUNT(company.country_id) as total'))
->groupBy('company.country_id')
->groupBy('country.country_name')
->get();
Table design:
1. Participate_company
competition_id (pk/fk)
company_id (pk/fk)
2. company
company_id (pk)
company_name
country_id (fk)
3. country
country_id (pk)
country_name
4. competition
competition_id (pk)
competition_year
I want to produce result of count distinct country based on competition year. For example competition year = 2012, country_name = England, count(total) = 20. But my current query produce null.
SQLFiddle : http://sqlfiddle.com/#!9/a2092f/1
I suggest using Laravel ORM Relationship and Eager Loading to solve this problem.
In Company model, we would define country()method:
public function country() {
return $this->belongsTo(Country::class, 'country_id', 'id');
}
In Competition model, define method
public function company() {
return $this->belongsToMany(Company::class);
}
So in controller you can call groupBy :
Competition::with('company:id,country_id')->get()->groupBy('year');
We will catch country_id in each company which is in relations of years.
I just tested a simple example, after that, we will loop over this collection and count them .
Hope this's usefull.
P/s. As using by models, my table's names: companies, countries, competitions, company_competition

How to get all column with some having same name when using laravel eloquent?

Let say I have 2 tables with column like this:
table_a: (with a corresponding model TableA)
id | (20+ other field) | deleted_at | created_at | updated_at
table_b: (with a corresponding model TableB)
id | a_id | ( 30+ other field ) | deleted_at | created_at | updated_at
Now I am using laravel and join on this two table
$result = TableA::join('table_b', 'table_a.id', '=', 'table_b.a_id')
->where('table_b.created_at', '>=', date('Y-m-d'))
->get();
The problem is that I am not able to some field of table_b when the column name are the same as table_a. (i.e. id, deleted_at, created_at and updated_at)
I have done some search, and from this question on stackoverflow, I may do:
$result = TableA::join('table_b', 'table_a.id', '=', 'table_b.a_id')
->where('table_b.created_at', '>=', date('Y-m-d'))
->select('table_b.id as b_id', 'table_b.created_at as b_created_at')
->get();
However, when using this method, I need to add all the column name into the select statement, and it is painful to do that on 50+ fields. It is an exporting function so all fields are necessary. Is there a way I can make the necessary rename without listing all other field that don't need a rename to retrieve all the rows? Or is there a way I can retrieve all the row without a rename?
P.S. I am using laravel 4. This is an old project and updating is even more painful than listing all fields.
Unfortunately Eloquent queries do not alias the fields from joined tables so the columns with the same name overwrite each other.
The obvious solution would be to rename the columns which is not always a viable option.
Another solution would be getting rid of joins and use whereHas method of a query builder, that allows filtering data based on data in related models. As it uses subselects instead of joins, no columns get overwritten.
All you'd need to do is defining a relation table_b in your TableA model with:
class TableA extends Model {
public function table_b() {
return $this->hasOne(TableB::class); //or hasMany, depending on your data model
}
}
and replacing:
$result = TableA::join('table_b', 'table_a.id', '=', 'table_b.a_id')
->where('table_b.created_at', '>=', date('Y-m-d'))
->get();
with
$result = TableA::whereHas('table_b', function($query) {
$query->where('created_at', '>=', date('Y-m-d');
});
That would give you all TableA records that have a related TableB record that matches given criteria.
// duplicates
$products = Product::orderBy('name')->get();
$ids = collect();
foreach ($products as $product) {
$count = Product::where('name', $product->name)->count();
if($count > 1) {
$ids->add($product->id);
}
}
$duplicates = Product::whereIn('id', $ids)->orderBy('name')->orderBy('id')->get();

Laravel Eloquent retrieve data from multiple tables

I have four tables
**Articles table**
id
title
body
owner_id
category_id
**Favorite articles table**
id
user_id
article_id
**User table**
id
user_name
user_type
**Category table**
id
category_name
How to get list of favorite articles (article_name,owner_name,category_name) which related to currently logged user from db using laravel eloquent?
Is it possible to do it in single line request? e.g.:
$articles_data=Auth::user()->favorite_articles->article...
EDIT
For the moment i have to use statement below:
$articles_data = FavoriteArticle::where('user_id', Auth::id())->join('articles', 'articles.id', '=', 'favorite_articles.article.id')
->join('users', 'users.id', '=', 'favorite_articles.user_id')
->join('categories', 'categories.id', '=', 'articles.id')
->get()
Which looks a bit complicated and doesn't use eloquent relations.
Completing #zippo_ answer, in the Controller you must reference what tables you want, e.g.
User.php
use Article;
public function article()
{
return $this->hasOne('App\Article');
}
and in the e.g. UserController.php
$user = User::with('article')->get();
EDIT:
if you want to relate User to Article.Category, after create a relation with user and article
Article.php
use Category;
public function category()
{
return $this->hasOne('App\Category');
}
e.g. UserController.php
$user_articles_categories = User::with('article.category')->get();
You can take advantage of laravel eager loading, which are also called as Eloquent relationships.
Eloquent relationships are defined as functions on your Eloquent model classes.
Eg. In Article Model
public function article()
{
return $this->hasOne('App\Model\Category');
}
In this way, you need to define all the relationships in the respective Model classes.
for more info: http://laravel.com/docs/5.1/eloquent-relationships

Categories