laravel querying many to many - php

I have 2 models, Service and Category. They are related with a many-to-many relationship like so:
Service.php
public function categories()
{
return $this->belongsToMany('App\Category')->withTimestamps();
}
Category.php
public function services()
{
return $this->belongsToMany('App\Service')->withTimestamps();
}
And of course they're joined by a pivot table:
category_service
- category_id
- service_id
- created_at
- updated_at
I'd like to use local scope to filter service result based on IDs of categories. I've done the following:
Service.php
public function scopeFilter($query, $category_ids)
{
$services = Service::whereHas('categories', function (Builder $query) use ($category_ids) {
$query->whereIn('category_id', $category_ids)->get();
});
return $services;
}
But I'm getting a Column not found error, specifically:
Column not found: 1054 Unknown column 'services.id' in 'where clause' (SQL: select * from `categories` inner join `category_service` on `categories`.`id` = `category_service`.`category_id` where `services`.`id` = `category_service`.`service_id` and `category_id` in (1, 2))
1 and 2 are the category IDs I pass.
I wrote the function based on the answer I found here and here.
Any pointers?

Your error message show that your query is begin with categories and without join services.
So put the ->get() outside the closure.
public function scopeFilter($query, $category_ids)
{
$services = Service::whereHas('categories', function (Builder $query) use ($category_ids) {
$query->whereIn('category_id', $category_ids);
})->get();
return $services;
}

Related

Search pivot table Eloquent

I have 3 tables
students
- id
- name
classes
- id
- name
student_class
- id
- student_id
- class_id
- score
I want to return a list of the students that belong to class_id = 100
$students = \Student::where(['class_id' => 100])->get();
this is my Student Class
use Illuminate\Database\Eloquent\Model;
class Student extends Model
{
protected $table = 'students';
public function store(Request $request)
{
$student = new Student;
$student->save();
}
}
the error I´m getting is:
<strong>Message:</strong> SQLSTATE[42S22]: Column not found: 1054 Unknown column 'class_id' in 'where clause' (SQL: select * from `students` where (`class_id` = 100))
update: I can do
$students = \Class::find(100)->with(['students'])->get();
and it will return all the students as a child of classes but I don´t need that.
I need the data from students and the pivot table (student_class) in particular de score column.
thank you for your help
Update your student model as
public function classes(){
return $this->belongsToMany(Class::class, 'student_class', 'student_id', 'class_id');
}
//query
Student::whereHas('classes', function ($query) {
$query->where('id', 100);
})->get();
UPDATE : in both of your model relation add
return $this->belongsToMany('App\Class')->withPivot('score');
now you can do this inside your loop
foreach ($student->classes as $class) {
echo $class->pivot->score;
}

Laravel Builder Scope with Union and many-to-many relationship

I have a notifications table (and model)
notifications table columns are thus:
id
title
body
is_public
...
I also have a users table (and model)
users table columns:
id
username
...
I also have a pivot notification_user table
columns:
user_id
notification_id
many-to-many relationship is set on both Notification and User models thus:
Notification.php
public function users()
{
return $this->belongsToMany('App\Api\V1\Models\User');
}
User.php
public function notifications()
{
return $this->belongsToMany('App\Api\V1\Models\Notification');
}
Now inside Notification.php I want to set a scope. In the scope I need to get public notifications and the current user's
private notifications in a single SQL query. from my table structure, public notifications are where is_public == 1. Private notifications are associated on the pivot table.
to achieve this, inside my Notification.php, I also have this setup:
public function scopePublicAndPrivate(Builder $query)
{
return $this->public($query)->union($this->private($query));
}
public function scopePublic(Builder $query)
{
return $query->where('is_public', 1);
}
public function scopePrivate(Builder $query)
{
$user = JWTAuth::parseToken()->authenticate(); //using JWT to get a user.
return $user->notifications();
}
Now when I try Notification::publicAndPrivate()->get() inside a controller, I get:
Illuminate\Database\QueryException with message 'SQLSTATE[21000]: Cardinality violation: 1222 The used SELECT statements have a different number of columns (SQL: (select * from `notifications` where `is_public` = 1) union (select * from `notifications` inner join `notification_user` on `notifications`.`id` = `notification_user`.`notification_id` where `notification_user`.`user_id` = 1))
Please I'll appreciate any help with getting this to work or a better solution.
I believe you should change:
return $user->notifications();
to something else, for example:
return $query->where('user_id', $user->id);
or maybe
return $query->whereHas('users', function($q) use ($user) {
$q->where('id', $user->id);
});
This is because in one query you are not using any join and in second you do and you are getting different number of columns for union parts.

How can I fetch Laravel models by matching a value on a relationship?

I have a User model with attributes like login, ID, age, etc. Another table is user_data with a residence column, for example.
How can I get all users with a specific residence? I have this:
User model:
public function user_data()
{
return $this->hasMany('App\Models\UserData');
}
public function FilterUser($request)
{
if ($request->has('city'))
{
$users = User::with('user_data')->where('residence', 'Warsaw')->get();
}
dd($users);
}
UserData model:
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
Now I get this error:
Column not found: 1054 Unknown column 'residence' in 'where clause' (SQL: select * from `users` where `residence` = warsaw and `users`.`deleted_at` is null)
And another question:
If User is my main model, should I connect it with relationships like above? hasMany() or belongsTo()?
Use the whereHas() query builder method:
$users = User::with('user_data')
->whereHas('user_data', function($query) {
$query->where('residence', 'Warsaw');
})
->get();
Edit - To use the value from $request you need to import the variable to the closure for whereHas():
...
->whereHas('user_data', function($query) use ($request) {
$query->where('residence', $request->city);
})
...
Edit - A query scope may be a better approach. In the User model, add this method:
function scopeInCity($query, $city)
{
return $query->whereHas('user_data', function($q) use ($city) {
$q->where('residence', $city);
});
}
Then you can filter all users by city (outside the model):
User::inCity($request->city)->get();
Use whereHas if you want to get all users that have a relationship with a specific attribute like so:
$users = User::whereHas('user_data', function($q) {
$q->where('residence', 'Warsaw');})->get();

Has many through

A Venue has many Subscriptions.
A Subscription has many Subscribers (User).
Theres a pivot table, containing the relation between user_id and subscription_id.
How can I get all Subscribers from a Venue?
I have tried with:
class Venue {
/**
* Members
*/
public function members() {
return $this->hasManyThrough('App\User', 'App\Subscription');
}
}
But it fails with MySQL error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'users.subscription_id' in 'on clause' (SQL: select `users`.*, `sub
scriptions`.`venue_id` from `users` inner join `subscriptions` on `subscriptions`.`id` = `users`.`subscription_id` where `
users`.`deleted_at` is null and `subscriptions`.`venue_id` = 1)
How my Subscription model look:
`Subscription`
class Subscription extends Model {
protected $table = 'subscriptions';
/**
* Subscripers
*/
public function subscribers() {
return $this->belongsToMany('App\User');
}
/**
* Venue
*/
public function venue() {
return $this->belongsTo('Venue');
}
}
Simple question: Why are you using a third model for Subscriptions? It sounds like a normal n:m relation between User and Venue, as already written in the comments above.
class User {
public function venues() {
return $this->belongsToMany('App\Venue');
}
}
class Venue {
public function users() {
return $this->belongsToMany('App\User');
}
}
This constellation actually needs three tables, which are (i gave each model a column name):
users
- id
- name
venues
- id
- name
user_venue
- user_id
- venue_id
But to access the relations, you can simply use the Eloquent magic:
// List of all venues (as Venue models) that are in relation with User with id $id
$venues = User::find($id)->venues()->get();
// Returns the alphabetically first user that has a relation with Venue with id $id
$user = Venue::find($id)->users()->orderBy('name', 'asc')->first();
If you need to store additional information in the pivot table (e.g. when the relation has been established), you can use additional pivot fields:
user_venue
- user_id
- venue_id
- created_at
class User {
public function venues() {
return $this->belongsToMany('App\Venue')->withPivot('created_at');
}
}
class Venue {
public function users() {
return $this->belongsToMany('App\User')->withPivot('created_at');
}
}
// Returns the date of the relations establishment for the alphabetically
// first Venue the User with id $id has a relation to
$created_at = User::find($id)->venues()->orderBy('name', 'asc')->first()->pivot->created_at;
I've never tried to do whatever you are trying to do there, because it seems (with the current information) conceptually wrong. I also don't know if it is possible to set up an own model for a pivot table, but I think it should work if the pivot table has an own primary id column. It could probably be helpful if you've a third model that needs to be connected with a connection of two others, but normally that doesn't happen. So try it with pivot tables, like shown above, first.
Alright, I still don't see a good use case for this, but I can provide you a query that works. Unfortunately I wasn't able to get an Eloquent query working, but the solution should be still fine though.
class Venue {
public function members($distinct = true) {
$query = User::select('users.*')
->join('subscription_user', 'subscription_user.user_id', '=', 'users.id')
->join('subscriptions', 'subscriptions.id', '=', 'subscription_user.subscription_id')
->where('subscriptions.venue_id', '=', $this->id);
if($distinct === true) {
$query->distinct();
}
return $query;
}
}
The relation can be queried just as normal:
Venue::find($id)->members()->get()
// or with duplicate members
Venue::find($id)->members(false)->get()

Left join with Where in Eloquent ORM

I'm trying to write this SQL query with Eloquent ORM but still no success:
SELECT *
FROM article
LEFT JOIN article_category
ON article.category_id = article_category.id
WHERE article_category.name_url = 'html'
LIMIT 10`
This is what I've came up with so far (I try to write it with only one query just like above):
ArticleCategory::where('name_url', '=', 'html')->with('articles')->get();
But it shows an error:
Column not found:
1054 Unknown column 'article.article_category_id' in 'where clause'
(SQL: select * from `article` where `article`.`article_category_id` in (1))
My models:
class Article extends Eloquent {
protected $table = 'article';
public function categories() {
return $this->belongsTo('ArticleCategory', 'category_id');
}
}
class ArticleCategory extends Eloquent {
protected $table = 'article_category';
public function articles() {
return $this->hasMany('Article');
}
}
You can change your relationship function to use the correct ID.
public function articles() {
return $this->hasMany('Article', 'category_id');
}
It expects the column category_id to actually be named article_category_id. It expects this because it is referencing the table artice_catigory, so article_category_id makes sense.
If possible, just rename your column in the table article to article_category_id and everything should be good.
You can use left join using eloquent orm as follows
Article::leftJoin('article_category', 'article.category_id', '=', 'article_category.id')
->select(['*'])->where('article_category.name_url','html')->take(10)->get();

Categories