Laravel Eloquent: Many-to-Many Select items without children (using pivot) - mysql

Pretty standard setup:
table "Posts",
table "Categories"
pivot "posts_categories"
/************** CATEGORY MODEL *********/
class Category extends Eloquent
{
/** standard code**/
public function posts()
{
return $this->belongsToMany('Post','posts_categories');
}
}
/************* POSTS MODEL ************/
class Post extends Eloquent
{
/** the usual **/
public function categories()
{
return $this->belongsToMany('Category', 'posts_categories');
}
pivot table with proper foreign keys setup.
I cannot seem to figure how to get ALL POSTS without any categories (or for that matter get categories without any posts, like empty categories)
I would like to do this in eloquent, but in sql it'd be something like:
SELECT *,
(SELECT COUNT(id) cnt
FROM posts_categories pc
WHERE pc.post_id = p.id) AS cnt
FROM posts p
HAVING cnt = 0;

You can use this to get all Posts that have no Category:
$posts = Post::has('categories', '=', 0)->get();

Related

laravel eloquent subquery same table different columns

i am trying to re-construct a working mysql query (or something better with the same result) in laravel eloquent. My db looks like this:
I have a words table with columns id and name.
I have a pivot table called synonyms with columns id, word_id and synonym_id.
The synonyms table links words together that are synonyms. It links for example a bike and cycle together. But also bike to cycle etc.
I did manage to build this query in my phpstorm editor and it works:
select * from words
where id in (
select synonym_id from synonyms
where word_id in (select id from words where name = 'bike')
)
or id in (
select word_id from synonyms
where synonym_id in (select id from words where name = 'bike')
)
It returns a result set containing cycle, tandem, velocipede etc.
I did create a "word" eloquent model that has these methods:
/**
* Synonyms for this word
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function synonyms() {
return $this->belongsToMany(Word::class, 'synonyms', 'synonym_id', 'word_id');
}
/**
* Words that have this word as their synonym
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function isSynonymFor() {
return $this->belongsToMany(Word::class, 'synonyms', 'word_id', 'synonym_id');
}
And now I am trying to rebuild that query in eloquent like this:
$word = 'bike'; //Just for debugging purposes I hardcoded it.
$query = Word::whereHas('isSynonymFor', function(Builder $query) use($word) {
$query->where('name', '=', $word);
})->orWhereHas('synonyms', function(Builder $query) use($word) {
$query->where('name', '=', $word);
})
return $query->get();
This does not work like I want it too. It just returns a collection with "bike" in it and not the rest.
How can I fix it?
If you only want to reproduce that exact query (no eloquent relationships used), you should be able to do it just fine with whereIn(column, Closure) for the subqueries.
$query = DB::table('words')
->whereIn('id', function ($sub) {
$sub->select('synonym_id')
->from('synonyms')
->whereIn('word_id', function ($sub2) {
$sub2->select('id')
->from('words')
->where('name', 'bike');
});
})
->orWhereIn('id', function ($sub) {
$sub->select('word_id')
->from('synonyms')
->whereIn('synonym_id', function ($sub2) {
$sub2->select('id')
->from('words')
->where('name', 'bike');
});
})
// ->toSql();
->get();

how to to make eloquent scope with whereHas like sql query below?

how to to make eloquent scope with whereHas like sql query below
table Property(id, title, slug, category_id, location_id,image)
table Category(id, name, slug)
table City ( id, name, slug)
The simple sql query that i need
Select * from property
join category on property.category_id=category.id
join city on property.location_id = city.id
where category.name = $query and city.name=$query
I want to make the eloquent scope in the Property Model
This is easy with relationships.
Based on your query, let's say this is your Property model:
class Property extends Model
{
public function category()
{
return $this->belongsTo(Category::class);
}
public function city()
{
return $this->belongsTo(City::class, 'location_id');
}
Now I can write my Eloquent query like this:
$query = 'Example';
$properties = Property::where('name', $query)
->whereHas('category', function (Builder $builder) use ($query) {
$builder->where('name', $query);
})->get();
Please note that Builder is imported from Illuminate\Database\Eloquent\Builder. You can also add with('category', 'city') to the query above in order to eager load those relationships.

Where not Exists en Laravel

Could anybody tell me what error I might have in my laravel query, basically what I want is to list the records of a table whose id is not related in another table. I did it in Mysql with this query: SELECT * FROM item WHERE NOT EXISTS (SELECT null FROM qualifications WHERE grades.item_id = item.id AND qualifications.user_id = 2);
but now I need to do this same query in laravel, I tried it this way:
codigo
and what I get is this syntax error that I do not know how to solve anymore:
error
I am very grateful to anyone who can tell me what I am doing wrong, or in what form I make that query in Laravel.
You can also rewrite your query as left join like
SELECT i.*
FROM item i
LEFT JOIN qualifications q ON q.item_id = i.id AND q.user_id = 2
WHERE q.item_id IS NULL
In query builder you can write it as
DB::table('item as i')
->select('i.*')
->leftJoin('qualifications as q', function ($join) use($user_id) {
$join->on('q.item_id', '=', 'i.id')
->on('q.user_id', '=', $user_id);
})
->whereNull('q.item_id')
->get();
Another approach which i suggest you to go with, is setup your relations and models and do it with eloquent way
class Item extends Model
{
public function qualifications()
{
return $this->hasMany(\App\Models\Qualification::class, 'item_id');
}
}
class Qualification extends Model
{
public function qualifications()
{
return $this->belongsTo(Item::class, 'item_id');
}
}
And then you can use Querying Relationship Absence
Item::whereDoesntHave('qualifications', function ($query) use($user_id) {
$query->where('user_id', '=', $user_id);
})->get();

Counting rows for the column in mysql

My problem is simple. I have two tables
transaction_bodies
------------------
body_id
full_name
and the other one is
transaction_accounts
--------------------
account_id
body_id
account_name
Relation is one to many. One body can have multiple accounts. I am trying to create a query that counts the accounts that bodies have.
I tried this
SELECT *
FROM
(
SELECT count(*) as trans, tb.full_name
FROM transaction_accounts ta
LEFT JOIN transaction_bodies tb
ON tb.body_id = ta.body_id
) as row;
But this doesn't give the right result. Can anyone help me out with this?
And if can provide how to write sub-queries in Laravel that would be a appreciated much.
Try this :
$result = DB::table('transaction_bodies')
->leftJoin('transaction_accounts as
ta','transaction_bodies.body_id','ta.body_id')
->select(DB::raw('count(ta.account_id) AS trans'),'transaction_bodies.full_name')
->groupBy('transaction_bodies.body_id')
->get();
You can do it with LEFT JOIN, e.g.:
SELECT tb.body_id, COUNT(ta.*)
FROM transaction_bodies LEFT JOIN transaction_accounts ta
ON tb.body_id = ta.body_id
GROUP BY tb.body_id;
With a simple LEFT JOIN you can achieve it like
SELECT tb.full_name, COUNT(account_id) as accounts
FROM transaction_bodies tb LEFT JOIN transaction_accounts ta
ON tb.body_id = ta.body_id
GROUP BY tb.body_id;
In Laravel you can do it like with model
$accounts = Transaction_body::leftJoin('transaction_accounts as ta','transaction_bodies.body_id','ta.body_id')->groupBy('transaction_bodies.body_id')->get();
without model
$accounts = DB::table('transaction_bodies')->leftJoin('transaction_accounts as ta','transaction_bodies.body_id','ta.body_id')->groupBy('transaction_bodies.body_id')->get();
/**
* Class Body
*/
class Body extends Model
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'transaction_bodies';
/**
* Get the accounts for the Transaction Body.
*/
public function accounts()
{
return $this->hasMany(Account::class);
}
}
/**
* Class Account
*/
class Account extends Model
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'transaction_accounts';
/**
* Get the body that owns the account.
*/
public function body()
{
return $this->belongsTo(Body::class);
}
}
//usage
$accounts = Body::find(1)->accounts;
https://laravel.com/docs/5.4/eloquent-relationships#one-to-many

CakePHP sub query SQL in HABTM relation

I want to suggest related products by tags and sort order by the most matched.
the HABTM model association between Product and Tag
class Product extends AppModel {
//..
var $hasAndBelongsToMany = array("Tag");
//..
}
and vice versa in Tag model. also join-table name is "products_tags".
for Ex.sample..
//just sample of Product contain Tag data
$products[0]['Tag'] = array('touch', 'phone', '3G', 'apple'); //iPhone
$products[1]['Tag'] = array('phone', '3G', 'BB'); //BB
$products[2]['Tag'] = array('touch', '3G', 'apple'); //iPad
$products[3]['Tag'] = array('3G', 'air card'); //3G air card
in this sample the most related to iPhone sort by priority are..
ipad (found in 3 tags)
BB (found in 2 tags)
aircard (only 1 matched)
How Cake's way using Model find() to get sub-query like this:
SELECT DISTINCT (product_id) AS id, (
/* sub-query counting same tags*/
SELECT COUNT(*) FROM tags as Tag
LEFT JOIN products_tags AS ProductTag ON ( Tag.id = ProductTag.tag_id )
WHERE product_id = id
AND Tag.name IN ( 'touch', 'phone', '3G', 'apple' )
) AS priority /*this is weight count by how many same tags*/
FROM `tags` as Tag
LEFT JOIN products_tags AS ProductTag ON ( Tag.id = ProductTag.tag_id )
WHERE Tag.name
IN ( 'touch', 'phone', '3G', 'apple' )
ORDER BY priority DESC
SQL query above return exactly what I needed. but I can't find the way to parse params to CakePHP's AppModel->find() method.
If the result of this sql does not use pagination (assuming that related products in a normal webpage are 3-5 entries) why don't you use this complex SQL instead?
i.e. create a function relatedProducts() in your Product model
then use $this->query($sql)
sample code will be:
class Product extends AppModel {
//..
var $hasAndBelongsToMany = array("Tag");
//..
function relatedProducts($parameters){
$sql = "select..... where ....$parameters...";
return $this->query($sql);
}
}
Then you can use it in the controller by
$this->Product->relatedProducts($some_parameters);