Why is Eloquent searching where Active =? - php

I have two models, App\Song (belongsTo App\Host) and App\Host (hasMany App\Song).
In my controller I am using the following query:
$songs = Song::whereHas('host', function($query) {
$query->eligable()->activeHost();
})->inDownloadedQueue()->get();
This is derived from the following query scopes in my Song.php model
public function scopeEligable($query)
{
$query->where('skip_threshold', '>', \DB::raw('songs.attempts'));
}
public function scopeActiveHost($query)
{
$query->where('active', 1);
}
public function scopeInDownloadQueue($query)
{
$query->whereNull('downloaded');
}
This doesn't return any results, turning to ->toSql() function to debug the query is as follows:
select * from "songs" where exists (select * from "hosts" where "songs"."host_id" = "hosts"."id" and "skip_threshold" > songs.attempts and "active" = ?) and "downloaded" is null
This host = ? seems to be the problem. Any ideas why this is?

The ? is a SQL parameter. Eloquent uses PDO parameterized queries, which helps prevent SQL injection.
The query itself will show active = ?, but when the query is executed, it will bind your value (1) to this parameter, and the query that is actually executed will be active = 1.
The queries are fine, you're just not getting any results because you don't have any songs in your downloaded queue that are related to eligible, active hosts.

Related

Laravel latest not working (not appearing in my SQL query)

So I have a Student model with this function:
public function latestStatus()
{
return $this->hasOne(StatusStudent::class)->latest();
}
then I just do a query with this latestStatus()
$query = Student::findOrFail(1);
$query = $query->whereHas('latestStatus', function($query) use ($statusuri) {
$query->where('status_id', 1);
});
dd($query->toSql());
and the toSql() function returns:
"select * from `students` where exists (select * from `status_student` where `students`.`id` = `status_student`.`student_id` and `status_id` = ?)
as if latest() is ignored.
Why doesn't latest() add anything to the query?
Thanks.
Edit:
I tried adding selectRaw for example:
public function latestStatus()
{
return $this->hasOne(StatusStudent::class)->selectRaw('MAX(status_student.id)');
}
and still nothing appears in my query.
If you dig deeper to the whereHas() relationship. It calls the has() method then if you look for the has() method you will see the getRelationWithoutConstraints() method, means that it will call the relationship but it will remove all the constraints attach to it and will only call the base query instance :
public function latestStatus()
{
return $this->hasOne(StatusStudent::class)->latest(); // the latest() will be removed in the query if you call the `latestStatus` using the `whereHas() or has()`
}
so if you use the whereHas() like the way you use it :
"select * from `students` where exists (select * from `status_student` where `students`.`id` = `status_student`.`student_id` and `status_id` = ?)
it will return the query with out the latest().
Instead of doing it like that you can do it like :
Student Model
public function status() : HasOne
{
return $this->hasOne(StatusStudent::class);
}
Controller
$student = Student::findOrFail(1);
$student->whereHas('status', function($query) {
$query->where('status_id', 1)
->latest();
})
But since the relationship is define as one-to-one :
$student = Student::findOrFail(1);
$student->load('status');
or
$student = Student::findOrFail(1)->status()->get();
Maybe you want to get the latest of all the status.
StudentStatus::query()->latest()->get();
As stated in a comment by #matticustard,
findOrFail() returns a model, not a query builder.
Instead of findOrFail(1) use where('id', 1)

Laravel query builder add complex query result

I have three models with the following hierarchy :
User
id
....some other properties
Journey
id
user_id
budget
....some other properties
Confirmation
id
journey_id
user_id
....some other properties
I have a HasMany from User to Journey, a HasMany from Journey to Confirmation.
I want to get the sum for a column of the journeys table by going through the confirmations table but I cannot create an intermediate HasManyThrough relation between User and Journey by using Confirmation.
I have tried to do
public function journeysMade(): HasManyThrough
{
return $this->hasManyThrough(Journey::class, Confirmation::class);
}
// And after,
User::with(...)->withSum('journeysMade','budget')
But it was not possible because the relations are not adapted.
With hindsight, the sql query I want to translate would look like
select coalesce(sum(journeys.budget), 0) as income
from journeys
inner join confirmations c on journeys.id = c.journey_id
where c.user_id = ? and c.status = 'finalized';
How can I implement this query considering how I will use my query builder :
$driversQueryBuilder = User::with(['profile', 'addresses']); // Here
$pageSize = $request->input('pageSize', self::DEFAULT_PAGE_SIZE);
$pageNumber = $request->input('pageNumber', self::DEFAULT_PAGE_NUMBER);
$driversPaginator = (new UserFilterService($driversQueryBuilder))
->withStatus(Profile::STATUS_DRIVER)
->withCountry($request->input('country'))
->withSex($request->input('sex'))
->withActive($request->has('active') ? $request->boolean('active') : null)
->get()
->paginate(perPage: $pageSize, page: $pageNumber);
return response()->json(['data' => $driversPaginator]);
The reason why I want to get a builder is because UserFilterService expects a Illuminate\Database\Eloquent\Builder.
Do you have any idea about how I can solve this problem ?
Not 100% sure what exactly you want to sum, but I think you need the following query
$user->whereHas('journeys', function($query) {
$query->whereHas('confirmations', function($subQuery) {
$subQuery->sum('budget);
}
});
If you the above query isn't summing the budget you need, you just add another layer of abstraction with whereHas methods to get exactly what you need. Hope this helps!
EDIT:
$user->whereHas('confirmations', function($q) {
$q->withSum('journeys', 'budget')->journeys_sum_budget;
}

Doctrine2 - How can I sort dynamicaly the results of a relation?

What I need :
I'm building an API that returns users and some relations : I have an entity called "User" which has a lot of relationships. Let's take the "comments" as example :
/**
* #ORM\OneToMany(targetEntity="Comments", mappedBy="idClient", cascade={"persist"})
*/
protected $comments;
In some cases, the client wants to get the user data and the comments data in the same query (by adding "comments" to the "include query param), and wants to sort the comments in a specific order. This order is provided by the client in the query params. In this example, the comments must be sorted by id ASC.
/api/users?include=comments&sort=comments.id
Note that order ASC is implicit in that case.
I have a search() function that build the query :
$qb = $this->createQueryBuilder($this->elementName);
/* SELECTs */
$selects = $this->getSelects($params);
foreach($selects as $select) {
$qb->addSelect($select);
}
/* WHEREs */
$wheres = $this->getWheres($params);
foreach($wheres as $where) {
$qb->andWhere($where);
}
/* ORDER BY */
foreach($sortBy as $column => $order) {
$qb->addOrderBy($column, $order);
}
/* LIMIT and OFFSET */
$qb->setFirstResult($offset)
->setMaxResults($limit);
$query = $qb->getQuery();
$results = $query->getResult();
This function is called to get the primary data of the request : the users data. Then, the users are transformed by a UserTransformer, in order to answer the client in a specific format (JSONAPI).
The relationships (as comments) are called later by querying the entity is the object transformer :
$comments = $user->getComments(); // Returning $this->comments in the User class.
return $this->collection($comments, new CommentsTransformer()); // sends the $comments data to the CommentsTransformer.
What I tried
I tried addOrderBy() to the query builder but I get an error because the DQL does not contains any association named comments.id :
Doctrine\ORM\Query\QueryException: [Semantical Error] line 0, col 110 near 'id ASC': Error: Class Foo\Users has no field or association named comments.id
Here is the DQL :
SELECT e FROM Foo\Users u WHERE [...] ORDER BY u.comments.id ASC
Is there any way I can "see" the comments properties and sort the comments on them in my Query ?
Or is there any way I can inject the sort order in my Users class so it can retrieve the comments data in that dynamical order ? like using $user->getComments($sortBy) and then catch the $sortBy in my Users class (or preferably on my entity mother class) and alter the build-in Doctrine request to add my sorting order ?
PS : sorry for (probably) bad english, it's not my mother tongue.
apply criteria in your getComments function like
use Doctrine\Common\Collections\Criteria;
public function getComments()
{
$criteria = Criteria::create()
->orderBy(['id' => Criteria::ASC]);
return $this->comments->matching($criteria);
}

Why getQuery ignores soft deletes?

In Laravel, when I use getQuery function to modify my query result based on model, I'm getting all values including softdeleted. It literally forgets to include and stock.deleted_at is null in the query. Why? How can I make it filter out deleted records.
Model
class Stock extends Model
{
use SoftDeletes;
protected $dates = ['issue_date', 'expiry_date'];
...
Query (getting stock grouped by expiry_date)
$query = Stock::where('product_id', $id);
$query = $query->getQuery();
$query
->select(DB::raw(
'count(*) as total,
DATE_FORMAT(IFNULL(`expiry_date`, "0000-00-00"),"%d-%m-%Y") AS expiry_date '
))
->groupBy('expiry_date');
$result = $query->get();
I had an idea of not using getQuery(), but in this case 'issue_date' will give me an error message saying "laravel Data missing".
Use $query->toBase() instead of $query->getQuery().
$results = Stock::where('product_id', $id)->toBase()->selectRaw('
count(*) as total,
DATE_FORMAT(IFNULL(`expiry_date`, "0000-00-00"),"%d-%m-%Y") AS expiry_date
')->groupBy('expiry_date')->get();
The getQuery method simply returns the underlying query, whereas toBase first applies all global scopes (soft deletes is implemented as a global scope).
BTW, you can call select and groupBy directly on the Eloquent query itself:
$results = Stock::where('product_id', $id)->selectRaw('
count(*) as total,
DATE_FORMAT(IFNULL(`expiry_date`, "0000-00-00"),"%d-%m-%Y") AS expiry_date
')->groupBy('expiry_date')->get();
...though that would return partial Eloquent models, which is not always a great idea.

select in application give me an empty array but in database give rows(Codeigniter)

My model:
public function getSolServicoById($id){
$select = 'SELECT * FROM solicitacao_servico WHERE id_solicitacao = "$id" LIMIT 1';
$query = $this->db->query($select);
return $query->result();
}
My controller:
public function editaSolicitacao($id){
$this->load->model('Pedido_Model','pedido');
echo $id;
$data = $this->pedido->getSolServicoById($id);
print_r($data);
}
When i select it on database i receive rows but when i select in application i get empty array and i don't know why it happen?!
Try this :
$select = "SELECT * FROM solicitacao_servico WHERE id_solicitacao = '{$id}' LIMIT 1";
Also look forward to using prepared statements to reduce sql-injection vulnerability.
A better way to do this, just because its a simple query in CI is:
$this->db
->select('*')
->from('solicitacao_servico')
->where('id_solicitacao',$id)
->limit(1)
->get();
Doing it this way doesn't constrain your code to a particular database type (MySQL, MSSQL, etc) because it will create the correct syntax for your application with the built in active record feature.

Categories