Laravel Eloquent - where relationship field does not equal - sql

I thought this would be fairly simple but it's not playing ball currently.
I have 2 tables for this question, 'applications' & 'application_call_logs'.
This query needs to return all from the applications table where the latest call log doesn't have a status of X.
Here's the current query:
$query = Application::query();
$query->where(function($query) {
$query->whereDoesntHave('call_logs');
$query->orWhereHas('latest_call_log', function($q) {
$q->where('status', '!=', 'not interested');
});
});
return $query->get();
This should return all rows that either have no call logs, or where the latest call log doesn't have the status field equaling a specific string.
This here:
$q->where('status', '!=', 'not interested');
Seems to have no affect if the call_logs has more than 1 row, even though I'm querying the latest relationship. I've also verified the latest is returning the correct latest record.
The two relationships in the Application model are:
public function call_logs()
{
return $this->hasMany('App\ApplicationCallLog', 'lead_id', 'id');
}
public function latest_call_log()
{
return $this->hasOne('App\ApplicationCallLog', 'lead_id', 'id')->latest();
}
Checked the SQL generated:
select * from `applications` where (not exists (select * from `lead_call_logs` where `applications`.`id` = `lead_call_logs`.`lead_id`) or exists (select * from `lead_call_logs` where `applications`.`id` = `lead_call_logs`.`lead_id` and `status` != ?))

there is a solution around should be good for this situation:
i think that this line has the week point of the code:
return $this->hasOne('App\ApplicationCallLog', 'lead_id', 'id')->latest();
this should be hasMany, but you use hasOne to limit the result to one.
and if you tried:
return $this->hasMany('App\ApplicationCallLog', 'lead_id', 'id')->latest()->limit(1);
it simply won't work, because the result will be limited to ApplicationCallLog for all of the results ....
will, there is a package staudenmeir/eloquent-eager-limit that is made especially for this situations:
composer require staudenmeir/eloquent-eager-limit:"^1.0"
class Application extends Model
{
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
public function latest_call_log()
{
return $this->hasMany('App\ApplicationCallLog', 'lead_id', 'id')->latest()
->limit(1);
}
}
class ApplicationCallLog extends Model
{
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
}
using this package will limit ApplicationCallLog for every result in your query not one for all of the result, and that will have the same effect for hasOne ....
with this minor enhancement, i think:
$q->where('status', '!=', 'not interested');
will work ...
more about eloquent-eager-limit package in:
https://github.com/staudenmeir/eloquent-eager-limit

Related

Transforming Raw Sql to Laravel equolent

I have written this SQL code
SELECT drugs.*, COUNT(*) as 'views' from drugs INNER JOIN drug_seen on drugs.id = drug_seen.drug_id GROUP BY drugs.id order by views ASC
And now I am trying to write in in the Laravel equolent but I am facing some troubles.
This is what I have tried
$drugs = Drug::select(DB::raw('drugs.*,count(*) as views'))
->join('drug_seen', 'drugs.id', 'drug_seen.drug.id')
->groupBy('drug.id')->orderByRaw('views');
I am having errors like column not found i think the code is not written properly
Drug class
class Drug extends Model
{
use HasFactory;
use SoftDeletes;
...
...
...
public function drugVisits()
{
return $this->hasMany(DrugSeen::class);
}
Hop this will solve your problem.
$drugs = Drug::with('drugVisits')->get();
$drugs->count(); //for total records in drugs table.
You have typo error in join instead on drug_id you use drug.id
Try this:
$drugs = Drug::select(DB::raw('drugs.*,count(*) as views'))
->join('drug_seen', 'drugs.id', 'drug_seen.drug_id')
->groupBy('drugs.id')->orderByRaw('views');
}
As soon as you use join() you're leaving Eloquent and entering Query\Builder, losing the benefits of Model configurations in the process. And with() eager-loads aren't the answer, if you're looking to filter the results by both tables. What you want is whereHas().
Also, as far as your grouping and count manipulation there, I think you're looking more for Collection handling than SQL groups.
$drugModel = app(Drugs::class);
$results = $drugModel->whereHas('drugVisits')->with('drugVisits')->get();
$organizedResults = $results
->groupBy($drugModel->getKey())
->sortyBy(function (Drugs $drugRecord) {
return $drugRecord->drugVisits->count();
});
If you want to have a 'views' property that carries the count in the root-level element, it would look like this:
$drugModel = app(Drugs::class);
$results = $drugModel->whereHas('drugVisits')->with('drugVisits')->get();
$organizedResults = $results
->groupBy($drugModel->getKey())
->map(function (Drugs $drugRecord) {
$drugRecord->views = $drugRecord->drugVisits->count();
return $drugRecord;
});
->sortyBy('views');

Laravel/SQL: How to fetch data from multiple table in a single query? that too using 'where'

Working on a search functionality on Laravel App(Blog/Posts).
There are multiple types of posts (each having a separate table in the database)
Like Business posts, Social Life posts etc..
Below is the search function on SearchController
class SearchController extends Controller
{
public function search(Request $request, $query = null)
{
if($query == null)
return redirect()->route('home');
$search = Business::where([['title','like','%'.$query.'%'],['status','=',1]])
->orWhere([['description','like','%'.$query.'%'],['status','=',1]])
->paginate(10);
return view('front.search',[
'results' => $search,
'query' => $query
]);
}
}
So basically my question is how to add other types of Post's table also?
My main motive is that when someone searches for anything, the result should be fetched from all types of posts table(business, nature, life & so on..).
You have to maintain common id in both the table
NOTE: Join is the preferable method
$querys = DB::table('Business')->where([['Business.title','like','%'.$query.'%'],['Business.status','=',1]])
->orWhere([['Business.description','like','%'.$query.'%'],['Business.status','=',1]]);
$querys->join('socialtable','socialtable.userid','=','Business.userid');
// Just join the social table
$querys->where('socialtable.title', 'like','%'.$query.'%');
$result = $querys->paginate(10);
If you have a model called Book, like this:
class Book extends Model
{
/**
* Get the author that wrote the book.
*/
public function author()
{
return $this->belongsTo('App\Author');
}
}
Then you can retrieve all of your books with authors like this:
$books = App\Book::with(['author'])->get();
Check out Eager loading from Laravel documentation.
Just add table name before every field
$querys = DB::table('Business')->where([['Business.title','like','%'.$query.'%'],['Business.status','=',1]])
->orWhere([['Business.description','like','%'.$query.'%'],['Business.status','=',1]]);
$querys->join('socialtable','socialtable.userid','=','Business.userid');
// Just join the social table
$querys->where('socialtable.title', 'like','%'.$query.'%');
$result = $querys->paginate(10);

Laravel 5 - Eloquent Query - Is this possible?

I want to find all of the "Posts" that have users. Users are assigned in groups and I want to find all of the "Posts" that have a particular user group. I can get the posts by name, and that have a usergroup:
$results = Post::PostBySlug("Testing")
->with('groups', 'groups.users')
->first();
This works, but, returns all of the user groups that are assigned to a particular group. I want to do it so that I can select the post, by the slug, and return all the users of a particular group. I have tried:
$results = Post::PostBySlug("Testing")
->with('groups', 'groups.users')
->where('groups.id', '=', 1);
But this did not return anything, only an unknown column. I have tried to do:
$results = Post::PostBySlug("Testing")
->with('groups AS testing', 'groups.users')
->where('testing.id', '=', 1);
But yet, no further. Can anyone suggest where I am going wrong, or suggest an alternative approach to this?
You need a query scope with the given group id.
class Post extends Model {
public function scopeInGroup($query, $group_id) {
return $query->whereHas('groups', function($query) use ($group_id) {
return $query->where('id', $group_id);
}
}
}
Then your query would be:
Post::PostBySlug('Testing')
->InGroup(1)
->with('groups.users')
->get();

Grails: "where" query with optional associations

I'm trying to run a "where" query to find a domain model object that has no association with another domain model object or if it does, that domain model object has a specific property value. Here's my code:
query = Model.where({
other == null || other.something == value
})
def list = query.list()
However, the resulting list only contains objects that match the second part of the OR statement. It contains no results that match the "other == null" part. My guess is that since it's checking a value in the associated object its forcing it to only check entries that actually have this associated object. If that is the case, how do I go about creating this query and actually having it work correctly?
You have to use a LEFT JOIN in order to look for null associations. By default Grails uses inner join which will not be joined for null results. Using withCriteria as below you should get the expected results:
import org.hibernate.criterion.CriteriaSpecification
def results = Model.withCriteria {
other(CriteriaSpecification.LEFT_JOIN){
or{
isNull 'id'
eq 'something', value
}
}
}
UPDATE
I know aliasing is not possible in DetachedCritieria where one would try to specify the join as in createCriteria/withCriteria. There is an existing defect regarding adding the functionality to DetachedCriteria. Just adding the work around for where query as mentioned in defect.
Model.where {
other {
id == null || something == value
}
}.withPopulatedQuery(null, null){ query ->
query.#criteria.subcriteriaList[0].joinType = CriteriaSpecification.LEFT_JOIN
query.list()
}
I would rather use withCriteria instead of the above hack.
this might work:
query = Model.where({
isNull( other ) || other.something == value
})
If that wouldn't work, try something like:
other.id == null || other.something == value
UPDATE:
or with good'ol criteria query:
list = Pack.withCriteria{
or{
isNull 'other'
other{ eq 'something', value }
}
}

Preserve Order of IN in ORM Order

I'm trying to do a query where I preserve the order of the ids in a IN statement. I can't seem to do it with either the Model Manage Query Builder or the standard ORM 'order' array parameter. Am I missing something? I keep getting:
UNEXPECTED TOKEN IDENTIFIER(, NEAR TO 'id`enter code here`,17743,16688,16650
Here's my model manager:
$query = $this->modelsManager->createQuery('SELECT * FROM Projects WHERE id IN ('.implode(',', array_keys($finalIterations)).')
ORDER BY FIELD(id,'.implode(',', array_keys($finalIterations)).'');
It's pretty obvious PhQL doesn't like the FIELD key word. Is there a way for me to do what I'm trying to do with PhQL? It seems I will not be able to do what I need to.
Unfortunately as previously said, this is missing a feature in Phalcon.
Have a look at this function, I've put it into my ModelBase abstract class which is parent class of all my models. It uses PhQL variable binding, so it's safe for handling direct user input.
You could have reimplemented custom \Phalcon\Mvc\Model\Criteria but this solution seems to be easier to work with, at least for me.
ModelBase abstract
public function appendCustomOrder( \Phalcon\Mvc\Model\CriteriaInterface &$criteria, $orderField, array &$orderValues = [] ) {
if(!empty($orderValues)) {
$queryKeys = $bindParams = [];
foreach($orderValues as $key => $id) {
$queryKey = 'pho'.$key;
$queryKeys[] = ':'.$queryKey.':';
$bindParams[$queryKey] = $id;
}
// TODO: add support for multiple orderBy fields
$criteria->orderBy('FIELD('.$orderField.','.implode(',',$queryKeys).')');
// there's no 'addBind' function, need to merge old parameters with new ones
$criteria->bind( array_merge( (array) #$criteria->getParams()['bind'], $bindParams ) );
}
}
Controller usage
$projectIDs = [17743, 16688, 16650];
$projectsModel = new Projects();
$criteria = $projectsModel->query->inWhere( 'id', $projectIDs );
$projectsModel->appendCustomOrder( $criteria, 'id', $projectIDs );
$projectsData = $criteria->execute();
This will generate valid PhQL syntax similar to this one:
SELECT `projects`.`id` AS `id`, `projects`.`title` AS `title`
FROM `projects`
WHERE `projects`.`id` IN (:phi0, :phi1, :phi2)
ORDER BY FIELD(`projects`.`id`, :pho0, :pho1, :pho2)