Laravel. How to get a record, that has all user ids in related table? - mysql

There is a chat table and intermediate table.
I need to get from the chat table only those chats for which all user identifiers are found.
Now I am getting records if at least one match is found
My query
$chat = $this->select('chats.*')->distinct()
->rightJoin("chat_user", function ($query) {
$query->on("chats.id", "=", "chat_user.chat_id")
->whereIn("chat_user.user_id", [2, 17]);
})
->where('chats.type', '=', 'single')
->get();
Result
But I need a chat width id 4, because only it matches my request
I also tried to do it like this
$chat = $this->select('chats.*')->distinct()
->rightJoin("chat_user", function ($query) use ($members_ids) {
$query->on("chats.id", "=", "chat_user.chat_id")
->where("chat_user.user_id", 2)
->where("chat_user.user_id", 17);
})
->where('chats.type', '=', 'single')->get();
But result is empty

In plain SQL, this could be achieved with the following query:
SELECT chats.*
FROM chats
WHERE chats.type = 'single'
AND EXISTS (SELECT 1 FROM chat_user WHERE user_id = 2 AND chat_id = chats.id)
AND EXISTS (SELECT 1 FROM chat_user WHERE user_id = 7 AND chat_id = chats.id)
Translated into a Laravel query, we get the following:
$userIds = [2, 7];
$chats = DB::table('chats')
->where('chats.type', 'single')
->where(function (Builder $query) use ($userIds) {
foreach ($userIds as $userId) {
$query->whereExists(fn (Builder $query) => $query
->select(DB::raw(1))
->from('chat_user')
->where('chat_user.user_id', $userId)
->whereColumn('chat_user.chat_id', 'chats.id'));
}
})
->select('chats.*')
->get();

You should do something like this instead:
$chat = $this->select('chats.*')->distinct()
->rightJoin('chat_user', function ($query) {
$query->on('chats.id', '=', 'chat_user.chat_id')
->where('chat_user.user_id', 2)
->where('chat_user.user_id', 17);
})
->where('chats.type', '=', 'single')
->get();
Specifying multiple where works as an AND condition. If you use whereIn() will work as an OR conditions for the supplied values.

Related

laravel eloquent query with relations

I'm trying to replace a mysql query with laravel eloquent. This is my structure.
Consumers Table
---------------
id, email, name
Transactions Table
-------------------
id, consumer_id, value, bonus_value
Output that I'm trying to achieve
id, email, name, total_value
1, abc#123.com, Al, 11000
2, abc2#123.com, Bl, 200000
This is what I have added in Consumer.php
/**
* Transactions Relationship
*
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function transactions(){
return $this->hasMany(Transaction::class, 'consumer_id');
}
And this is the query I've written so far.
$query = Consumer::query()
->join('consumer_transactions AS ct', function($q) {
$q->on('consumers.id', '=', 'ct.consumer_id')
->where('ct.status', 'processed')
->where('ct.approved', 1)
->select(DB::raw('SUM(ct.value + ct.bonus_value) AS total_points'))
;
})
->whereIn('consumers.id', $consumerIds)
->get()
;
It doesn't return total_points
Join Clause that passed to your join take Illuminate\Database\Query\JoinClause witch doesn't have a method called 'select'.
you select should be out of join clause.
$query = Consumer::query()
->join('consumer_transactions AS ct', function($q) {
$q->on('consumers.id', '=', 'ct.consumer_id')
->where('ct.status', 'processed')
->where('ct.approved', 1);
})
->select( ['consumers.*', DB::raw('SUM(ct.value + ct.bonus_value) AS total_points')])
->whereIn('consumers.id', $consumerIds)
->get();
I'm not fun of joining tables. That's why I can offer you a different approach.
$consumers = Consumer::whereIn('id', $consumerIds)
->with(['transactions' => function($query) {
$query
->where('startus', 'processed')
->where('approved', 1)
}])->get()
->map(function($item, $key) {
return $item->total_points = $item->transactions->value + $item->transactions->bonus_value
});

how to run mysql query in Laravel

I want to transform my MySql query into a Query in Laravel but I really don't know how to do this. I don't know how to rename in FROM like in SQL
My query is the following one :
SELECT f2.* FROM formation f2 WHERE f2.theme_id IN
(SELECT f.theme_id FROM user_formation uf JOIN formation f ON uf.formation_id = f.id WHERE uf.user_id = 2)
AND f2.id NOT IN
(SELECT formation_id FROM user_formation WHERE user_id = 2);
I tried something like this but ...
$q = Formation::query()
->from('formation AS f2')
->whereIn('f2.theme_id', function($r)
{
$r->select('f.theme_id')->from('user_formation AS uf')
->join('formation', function($join)
{
$join->on('uf.formation_id', '=', 'f.id')
->where ('uf.user_id', '=', $id)
});
});
->whereNotIn('f2.id', function($s){
$s->select('formation.id')
->from('user_formation')
->where('user_id', '=', $id)
})->get();
thanks for help.
If you want to run this raw query you can run:
$res = DB::select('
SELECT f2.*
FROM formation f2
WHERE f2.theme_id IN
(SELECT f.theme_id FROM user_formation uf JOIN formation f ON uf.formation_id = f.id WHERE uf.user_id = 2)
AND f2.id NOT IN
(SELECT formation_id FROM user_formation WHERE user_id = 2)');
Or you can rewrite this query in laravel query builder Eloquent ORM:
Formations::query()
->whereIn('formations.theme_id', function($q){
$user_formations_table = (new UserFormation)->getTable();
$formation_table = (new Formation)->getTable();
$q->select('paper_type_id')
->from($user_formations_table)
->join($formation_table, "$user_formations_table.formation_id", '=', "$formation_table.id")
->where("$user_formations_table.user_id", 2);
})->whereNotIn('formations.id', function($q){
$user_formations_table = (new UserFormation)->getTable();
$q->select('formation_id')
->where("$user_formations_table.user_id", 2);
})
->get();
Note that I have used models Formations, UserFormation, Formation Because you have used 3 different tables, you should add this models and specify tables to run ORM query
I advice to run first RAW query if there is no another need to run it with Eloquent
Hope this helps you
First of all, you need to fix your code indentations so you don't confuse yourself. Second, you placed semicolon in the wrong places. Third, you need to pass $id inside function because of the variable scope.
$q = Formation::query()
->whereIn('f2.theme_id', function($r) use ($id) {
$r->select('f.theme_id')->from('user_formation AS uf')
->join('formation', function($join) use ($id) {
$join->on('uf.formation_id', '=', 'f.id')
->where('uf.user_id', '=', $id);
}
);
})
->whereNotIn('f2.id', function($s) use ($id) {
$s->select('formation.id')
->from('user_formation')
->where('user_id', '=', $id);
})->get();
Note : If you are using VSCode, I suggest to use PHP Intelephense as it will help with autocomplete, syntax check, etc.

MySql group by with where condition not working

I have 2 tables, groups and questions. I need the result in such a way that it contains group name and corresponding non-deleted question count.
The structure and data for the two tables are as afollows.
My expected Result is as follows
I tried the following code, but it gives the entire row including the deleted questions.
$groupname = DB::table('groups as d')
->select([
'd.id','d.group_name',DB::raw("count(dtls.survey_group_id) as count")
])
->leftJoin('questions as dtls','d.id', '=', 'dtls.survey_group_id')
->whereNotExists( function ($query) {
$query->select(DB::raw(1))
->from('questions')
->where('id', 'd.id')
->where('dtls.is_deleted', 1);
})
->groupBy('d.id','d.group_name')
->get()
->toArray();
A problem here is that ->where('id', 'd.id') mathches id with the literal string d.id which obviously is not what you want. In addition, inner query tables should be aliased to prevent ambiguity. You can try changing it to:
$groupname = DB::table('groups as d')
->select([
'd.id','d.group_name',DB::raw("count(dtls.survey_group_id) as count")
])
->leftJoin('questions as dtls','d.id', '=', 'dtls.survey_group_id')
->whereNotExists( function ($query) {
$query->select(DB::raw(1))
->from('questions as innerDtls')
->whereRaw('innerDtls.id = d.id')
->where('dtls.is_deleted', 1);
})
->groupBy('d.id','d.group_name')
->get()
->toArray();
Alternatively your current query can be simplified to:
$groupname = DB::table('groups as d')
->select([
'd.id','d.group_name',DB::raw("count(dtls.survey_group_id) as count")
])
->leftJoin('questions as dtls','d.id', '=', 'dtls.survey_group_id')
->where('dtls.is_deleted', 0)
->groupBy('d.id','d.group_name')
->get()
->toArray();

Why is DB::raw variable being amended from a string to an integer in Laravel 5.7

I have the following part of a larger elequent query, which all works when I'm not injecting a variable into DB::raw.
$tests->join('test_categories', function ($join) use($request) {
$join->on('test_categories.id', '=', DB::raw('(select category_id
from test_category
join test_categories on test_category.category_id = test_categories.id
where test_category.test_id = tests.id
and test_categories.name = ?
limit 1)', ['Cars']
));
});
But when the SQL executes it amends the '?' to 4, as in:
and test_categories.name = 4
Rather than being the expected:
and test_categories.name = 'Cars'
Any help would be appreciated.
UPDATED
I also tried the following before:
$tests->join('test_categories', function ($join) use ($category) {
$join->on('test_categories.id', '=', DB::raw("(select category_id
from test_category
join test_categories on test_category.category_id = test_categories.id
where test_category.test_id = tests.id
and test_categories.name = :category
limit 1)",array('category' => $category)));
});
But in the query, this just outputs the following:
and test_categories.name = :category
This comes with a "SQLSTATE[HY093]: Invalid parameter number: mixed named and positional parameters" error.
Still the same issue.
And another variation which tries the same technique with the $join->on function.
$tests->join('test_categories', function ($join) use ($category) {
$join->on('test_categories.id', '=', DB::raw("(select category_id
from test_category
join test_categories on test_category.category_id = test_categories.id
where test_category.test_id = tests.id
and test_categories.name = :category
limit 1)"),array('category' => $category));
});
This results in an ErrorException (E_NOTICE) Array to string conversion:
protected function compileWheresToArray($query)
{
return collect($query->wheres)->map(function ($where) use ($query) {
return $where['boolean'].' '.$this->{"where{$where['type']}"}($query, $where);
})->all();
}
Based on this link you could use
$results = DB::select( DB::raw("SELECT * FROM some_table WHERE some_col = :somevariable"), array(
'somevariable' => $someVariable,
));
Try replacing :category with ".$category.".
Not sure if it's a good practice to do it this way but it should work.
$tests->join('test_categories', function ($join) use ($category) {
$join->on('test_categories.id', '=', DB::raw("(select category_id
from test_category
join test_categories on test_category.category_id = test_categories.id
where test_category.test_id = tests.id
and test_categories.name = ".$category."
limit 1)"));
});

Laravel count with where on Query Builder with joins

Good day all, I am trying to count all records in a table but only if the table does not contain data in a specific column (deleted_at). It is a join table the table names are companies and employees. I am currently counting the records with a DB::raw but it should only count it if the deleted_at column is null. Please understand that i am a beginner.
public function index()
{
$user = Auth::user()->id;
$companies = DB::table('companies AS c')
->select([
'c.id',
'c.logo',
'c.company_name',
'c.created_at',
'c.sector',
'c.deleted_at',
DB::raw('COUNT(e.id) AS employee_count')
])
->leftJoin('employees AS e', 'e.company_id', '=', 'c.id' )
->leftJoin('company_user AS cu', 'cu.company_id', '=', 'c.id')
->where('cu.user_id', '=', $user)
->where('c.deleted_at', '=', null)
->groupBy('c.id')
->get();
return view('account.companies.index')
->with('companies', $companies);
}
If you are using Mysql then you could use conditional aggregation
$companies = DB::table('companies AS c')
->select([
'c.id',
'c.logo',
'c.company_name',
'c.created_at',
'c.sector',
'c.deleted_at',
DB::raw('SUM(c.deleted_at IS NULL) AS employee_count')
])
->leftJoin('employees AS e', 'e.company_id', '=', 'c.id' )
->leftJoin('company_user AS cu', 'cu.company_id', '=', 'c.id')
->where('cu.user_id', '=', $user)
->groupBy('c.id')
->get();
In mysql when an expression is used inside sum(a= b) it will result as a boolean 0/1 so you can get your conditional count using above
Or you could use whereNull() method in your query
->whereNull('c.deleted_at')
Use this code:
$employeeCount = DB::table('employees')
->select('companies.name', DB::raw('count(employees.id) as employee_count'))
->join('companies', 'employees.company','=','companies.id')
->groupBy('companies.id')
->get();