I'm trying to fetch some data with a subquery using Eloquent but dding returns nothing. Separately, this
$discountArticles = $discountTableItemIdIn
->where('recipient_type', '=', 'article')
->toArray();
or this
$discountArticles = $discountTableItemIdIn
->where('recipient_id', '=', $articleId)
->toArray();
work fine.
However when I try something like this, it fails (or rather, returns nothing):
$discountArticles = $discountTableItemIdIn->where(function ($subQuery) {
$subQuery
->where('recipient_type', '=', 'article')
->where('recipient_id', '=', $articleId);
})->toArray();
I know I can do separate queries on the same collection and do an array_merge but I'd like to get this way working instead. Not sure what's happening.
So $discountTableItemIdIn is a collection of the entire table? That means you're gonna need a different function, as the ->where() logic on a collection is different from how it functions on a builder (eloquent) instance.
Try using filter():
$discountArticles = $discountTableItemIdIn->filter(function ($item) use($articleId) {
return $item->recipient_type == "article" && $item->recipient_id == $articleId;
})->toArray();
What this will do is filter your $discountTableItemIdIn collection for records that have a type of article and a recipient_id of whatever $articleId contains, return a new collection and convert that to an array.
Just a note, this is quite inefficient; you should try to avoid loading the whole table into a collection and just query the table directly using the subquery logic in your question.
Related
If I have a collection with many levels, are there any tricks to find out what I need to put in my where() to be able to search by it? This is what I have so far, but I get an empty array from it. $contacts->where('contacts.tags.id', $tag->id) I've also tried contacts.tags.tags.id This is what I get if I run dd($contacts)
You may be looking for Eloquent - Eager Loading With Constraints.
$id = 'Your ID you are searching for';
$contacts= App\Models\Contact::with(['tags' => function ($query) {
$query->where('id', '=', $id');
}])->get();
show me all the contacts where tags = id. if it's only one you may switch ->get() for ->first() to return the object instead of a collection (array)
I have a table called List which i planned to be displayed into view with this command : $lists= List::with('user', 'product.photodb', 'tagCloud.tagDetail')->get();. But, i want the data displayed is only those that has TagID equal to the one user inputted. Those data can be retrieved from TagCloud table.
What i am currently doing is :
$clouds = TagCloud::select('contentID')
->where('tagDetailID', '=', $tagID)
->get();
$lists = List::with('user', 'product.photodb', 'tagCloud.tagDetail')
->where('id', '=', $clouds->contentID)
->get();
But when i tried to run it, it only return a null value, even though when i am doing return $clouds, it does returned the desired ID.
Where did i do wrong ? Any help is appreciated !
A couple of gotchas with your current solution.
Using get() returns an Illuminate\Database\Eloquent\Collection object. Hence you can't use $clouds->contentID directly since $clouds is a collection (or array if you prefer). See Collection Documentation.
where(...) expects the third parameter to be a string or integer, aka single value. Instead, you are passing a collection, which won't work.
The correct way is to use whereHas() which allows you to filter through an eager loaded relationship.
Final Code:
$lists = List::with('user', 'product.photodb', 'tagCloud.tagDetail')
->whereHas('tagCloud',function($query) use ($tagID) {
return $query->where('contentID','=',$tagID);
})
->get();
See WhereHas Documentation.
What you want is whereHas()
$list = List::with(...)
->whereHas('relation', function($q) use($id) {
return $q->where('id', $id);
})->get();
Apply Where condition in you tagCloud model method tagDetail
public function tagDetail(){
return $q->where('id', $id);
}
Is it possible to start an eloquent query, assign it to a variable then continue using the variable for two separate queries without them conflicting with one another. A simple example:
$students = $this->student
// more query stuff
->where('is_active', 1);
$bachelorStudents = $students
->where('course_id', 3)
->get();
$masterStudents = $students
->where('course_id', 4)
->get();
or would I need to do:
$bachelorStudents = $this->student
->where('course_id', 3)
->get();
$masterStudents = $this->student
->where('course_id', 4)
->get();
I always thought I could do the former, but some of my results appear to show I can't but I am open to believe that if you can do it then perhaps I'm doing something wrong.
When you're calling
$students = $this->student->where('is_active', 1);
you're creating a query builder object. Calling where*() on this object updates the object by adding given criteria. Therefore it's not possible to achieve what you want in your first code snippet, because when you call
$masterStudents = $students
->where('course_id', 4)
->get();
the query builder already contains where('course_id', 3) constraint added when you bachelorStudents.
Once you do that:
$students = $this->student->where('is_active', 1);
$stundents will contain a query builder with your where clause
If you do:
$bachelorStudents = $students->where('course_id', 3)->get();
You'll add another where clasuse to the $students builder, and this should work as you expect
But, when you do:
$masterStudents = $students->where('course_id', 4)->get();
You are adding another where clasuse to the same $students builder, thus resulting the query builder to be something like this:
$students->where('is_active', 1)
->where('course_id', 3)
->where('course_id', 4)
->get();
That probably isn't what you expect, because you have 2 where clauses with different course_id values
Think of $student as an object you modify everytime you add a clause, so you can use it for progressive query building, but remember that once you've added a clause to the query builder, the object is modified and the clause will be keept in the builder, so when you re-use the builder it will contain all the clasuses you previously added
Also, Rembember that when you need to apply some pre-defined filters to your query, in Laravel you should use query scopes
While everyone is explaining query builder and how it works, here's your answer.
1) Start off your query builder
$studentsQuery = $this->student
//Start a new query builder (optional)
->newQuery()
->where('is_active', 1);
2) Clone the initial query builder to our separate queries
$bachelorStudentsQuery = clone $studentsQuery;
$masterStudentsQuery = clone $studentsQuery;
3) Assign your where conditions and get the results
$bachelorStudentsResult = $bachelorStudentsQuery->where('course_id', 3)
->get();
$masterStudentsResult = $masterStudentsQuery->where('course_id',4)
->get();
Your use case is too simple for cloning.
It might help you DRY your code when lots of method chaining has been performed, especially when applying filters to queries.
How to merge this two queries ?
$data = DB::table('category_to_news')
->where('category_to_news.name', ucwords($category))
->remember(1440)
->count();
and
$data = DB::table('category_to_news')
->where('category_to_news.name', ucwords($category))
->remember(1440)
->get();
So, as far as I understand from your comment, you simply want to get all records from the table category_to_news and you want to know how many records are in there, right?
MySQL's count is an aggregate functions, which means: It takes a set of values, performs a calculation and returns a single value. If you put it into your names-query, you get the same value in each record. I'm not sure if that has anything to do with 'optimization'.
As already said, you simply run your query as usual:
$data = DB::table('category_to_news')
->where('name', ucwords($category))
->remember(1440)
->get(['title']);
$data is now of type Illuminate\Support\Collection which provides handy functions for collections, and one them is count() (not to be confused with the above mentioned aggregate function - you're back in PHP again, not MySQL).
So $data->count() gives you the number of items in the collection (which pretty much is an array on steroids) without even hitting the database.
Hi DB class dont return collection object it give error "call member function on array" but eloquent return collection object. for above code we can use collect helper function to make it collection instance then use count and other collection methods https://laravel.com/docs/5.1/collections#available-methods .
$data = DB::table('category_to_news')
->where('name', ucwords($category))
->remember(1440)
->get();
$data = collect($data);
$data->count();
You my get it using:
$data = DB::table('category_to_news')
->where('name', ucwords($category))
->remember(1440)
->get();
To get the count, try this:
$data->count();
Why you are using DB::table(...), instead you may use Eloquent model like this, create the model in your models directory:
class CategoryToNews extends Eloquent {
protected $table = 'category_to_news';
protected $primaryKey = 'id'; // if different than id then change it here
}
Now, you may easily use:
$data = CategoryToNews::whereName(ucwords($category))->get();
To get the count, use:
$data->count();
Of course I can use order_by with columns in my first table but not with columns on second table because results are partial.
If I use 'join' everything works perfect but I need to achieve this in eloquent. Am I doing something wrong?
This is an example:
//with join
$data = DB::table('odt')
->join('hdt', 'odt.id', '=', 'hdt.odt_id')
->order_by('hdt.servicio')
->get(array('odt.odt as odt','hdt.servicio as servicio'));
foreach($data as $v){
echo $v->odt.' - '.$v->servicio.'<br>';
}
echo '<br><br>';
//with eloquent
$data = Odt::get();
foreach($data as $odt){
foreach($odt->hdt()->order_by('servicio')->get() as $hdt){
echo $odt->odt.' - '.$hdt->servicio.'<br>';
}
}
In your model you will need to explicitly tell the relation to sort by that field.
So in your odt model add this:
public function hdt() {
return $this->has_many('hdt')->order_by('servicio', 'ASC');
}
This will allow the second table to be sorted when using this relation, and you wont need the order_by line in your Fluent join statement.
I would advise against including the order by in the relational method as codivist suggested. The method you had laid is functionally identical to codivist suggestion.
The difference between the two solutions is that in the first, you are ordering odt ( all results ) by hdt.servicio. In the second you are retrieving odt in it's natural order, then ordering each odt's contained hdt by servico.
The second solution is also much less efficient because you are making one query to pull all odt, then an additional query for each odt to pull it's hdts. Check the profiler. Considering your initial query and that you are only retrieving one column, would something like this work?
HDT::where( 'odt_id', '>', 0 )->order_by( 'servico' )->get('servico');
Now I see it was something simple! I have to do the query on the second table and get contents of the first table using the function odt() witch establish the relation "belongs_to"
//solution
$data = Hdt::order_by('servicio')->get();
foreach($data as $hdt){
echo $hdt->odt->odt.' - '.$hdt->servicio.'<br>';
}
The simple answer is:
$data = Odt::join('hdt', 'odt.id', '=', 'hdt.odt_id')
->order_by('hdt.servicio')
->get(array('odt.odt as odt','hdt.servicio as servicio'));
Anything you can do with Fluent you can also do with Eloquent. If your goal is to retrieve hdts with their odts tho, I would recommend the inverse query for improved readability:
$data = Hdt::join('odt', 'odt.id', '=', 'hdt.odt_id')
->order_by('hdt.servicio')
->get(array('hdt.servicio as servicio', 'odt.odt as odt'));
Both of these do exactly the same.
To explain why this works:
Whenever you call static methods like Posts::where(...), Eloquent will return a Fluent query for you, exactly the same as DB::table('posts')->where(...). This gives you flexibility to build whichever queries you like. Here's an example:
// Retrieves last 10 posts by Johnny within Laravel category
$posts = Posts::join('authors', 'authors.id', '=', 'posts.author_id')
->join('categories', 'categories.id', '=', 'posts.category_id')
->where('authors.username', '=', 'johnny')
->where('categories.name', '=', 'laravel')
->order_by('posts.created_at', 'DESC')
->take(10)
->get('posts.*');