Fetching related objects - doctrine

In Symfony 2 book there is an example of how to do that for ONE $product: http://symfony.com/doc/2.0/book/doctrine.html#fetching-related-objects
It is quite simple:
public function showAction($id)
{
$product = $this->getDoctrine()
->getRepository('AcmeStoreBundle:Product')
->find($id);
$categoryName = $product->getCategory()->getName();
// ...
}
But what if i want to fetch ALL products with category info joined automatically to each project?
Thank you!

This will do the trick:
$products = $this->getDoctrine()->getRepository('AcmeStoreBundle:Product')->findAll();
However, each time you do a getCategory on a product a sql query will be triggered which could result in performance issues.
What you really want to do is to make yourself a ProductManager service and write an explicit query joining product and category. So only one sql query will be generated. The Doctrine 2 manual has plenty of examples.
http://docs.doctrine-project.org/projects/doctrine-orm/en/2.1/reference/query-builder.html

You can either iterate over the products and get the categories from there. Adjusting the fetch mode on the relations might help in reducing the amount of queries performed.
However, you might also just write a custom DQL query to get what you need. It might look like something like this:
SELECT p, c
FROM AcmeStoreBundle:Product p
INNER JOIN p.category c

Related

laravel eloquent chunk ->with method

It seems that eloquent uses a single query for "with" regardless of how many ids there are
Book::with('author')->get();
This would trigger those two queries:
SELECT * FROM books;
SELECT * FROM authors WHERE id IN (...);
The second query may have thousands of author ids in the where clause which might cause problems with performance.
Is there some way so it would chunk that when using with?
I am aware that it is generally not a good idea to query such big result sets.
Yes there are, and its clear in the documentation, you can do something like this:
use App\Models\Flight;
Flight::chunk(200, function ($flights) {
foreach ($flights as $flight) {
//
}
});
Or you can also do this:
use App\Models\Flight;
foreach (Flight::lazy() as $flight) {
//
}
You can get the details in the documentation to learn more:
https://laravel.com/docs/9.x/eloquent#chunking-results
if you need only Books and count authors you can use withCount method
Book::withCount('author')->get();

Laravel, Many-to-many relationships, find() vs where()

I have a Laravel 4.2 site with a pretty simple database layout. The important part is a People model and a Subject model that have a many-to-many relationship. This works, so that, for instance:
$id = 5;
$ppl = Subject::find($id)->people()->orderBy('lastname')->get();
Returns all People for a given Subject. What I'm trying to do is instead of finding all the People for a single subject, to find all the people for multiple subjects. My guess was something like this:
$subjects = array(5, 6, 7);
$ppl = Subject::whereIn('id', $subjects)->people()->orderBy('lastname')->get();
That doesn't work (undefined method people()). Neither does the following (undefined property people):
$ppl = Subject::whereIn('id', $subjects)->people->orderBy('lastname')->get();
I'm currently just using raw SQL t get around this. How can I use eloquent relationships with where() or whereIn() calls on a model? Or, is there just a better eloquent way of approaching this problem.
Edit: Here's the raw SQL I used to get a list of the people.id's for a given array of subjects:
SELECT
DISTINCT(people.id)
FROM people
LEFT JOIN person_subject ON person_subject.person_id=people.id
WHERE
person_subject.subject_id IN (%s) AND
deleted_at IS NULL
Your eloquent relationships should allow you to eagar load the people related to a subject, you can do something like this:
Subject::whereIn('id', $subjects)->with('people')->orderBy('lastname')->get();
This will return your people related to the subjects.
Let me know if this doesn't work.
What you need is to join the tables to order by the lastname:
$ppl = Subject::whereIn('id', $subjects)
->select('subjects.*')
->distinct()
->join('people', 'people.id', '=', 'subjects.people_id')
->orderBy('lastname')
->get();
Check all your tables names because I don't know them and above are just an example one.

Is it possible to eager load arbitrary queries in Eloquent?

I'm working in Laravel 4, and I have a Child model with multiple EducationProfiles:
class Child extends EloquentVersioned
{
public function educationProfiles()
{
return $this->hasMany('EducationProfile');
}
}
If I wanted to get all the EducationProfiles for each kid under age 10 it would be easy:
Child::where('date_of_birth','>','2004-03-27')->with('educationProfiles')->all();
But say (as I do) that I would like to use with() to grab a calculated value for the Education Profiles of each of those kids, something like:
SELECT `education_profiles`.`child_id`, GROUP_CONCAT(`education_profiles`.`district`) as `district_list`
In theory with() only works with relationships, so do I have any options for associating the district_list fields to my Child models?
EDIT: Actually, I was wondering whether with('educationProfiles') generates SQL equivalent to:
EducationProfile::whereIn('id',array(1,2,3,4))
or whether it's actually equivalent to
DB::table('education_profiles')->whereIn('id',array(1,2,3,4))
The reason I ask is that in the former I'm getting models, if it's the latter I'm getting unmodeled data, and thus I can probably mess it up as much as I want. I assume with() generates an additional set models, though. Anybody care to correct or confirm?
Ok, I think I've cracked this nut. No, it is NOT possible to eager load arbitrary queries. However, the tools have been provided by the Fluent query builder to make it relatively easy to replicate eager loading manually.
First, we leverage the original query:
$query = Child::where('date_of_birth','>','2004-03-27')->with('educationProfiles');
$children = $query->get();
$eagerIds = $query->lists('id');
Next, use the $eagerIds to filterDB::table('education_profile') in the same way that with('educationProfiles') would filter EducationProfile::...
$query2 = DB::table('education_profile')->whereIn('child_id',$eagerIds)->select('child_id', 'GROUP_CONCAT(`education_profiles`.`district`) as `district_list`')->groupBy('child_id');
$educationProfiles = $query2->lists('district_list','child_id');
Now we can iterate through $children and just look up the $educationProfiles[$children->id] values for each entry.
Ok, yes, it's an obvious construction, but I haven't seen it laid out explicitly anywhere before as a means of eager loading arbitrary calculations.
You can add a where clause to your hasMany() call like this:
public function educationProfilesUnderTen() {
$ten_years_ago = (new DateTime('10 years ago'))->format('Y-m-d');
return $this->hasMany('EducationProfile')->where('date_of_birth', '>', $ten_years_ago)
}

How To Use Magento Collections to Run Query, Get Results, and Print Them

I am fairly new to programming, php, Magento, and most of all, SQL. Please forgive me if this is somehow a dumb question.
I am trying to use Magento collections to select two different columns in two different tables and join them. These two columns contain product numbers that follow the same conventions and my goal is to get and display the product numbers that field a (lets call it 'product_id') contains that field b ('item_nr') does not.
Here is my function so far, located currently in a model that will be called by a controller action.
public function importCompare() {
$orderlistCollect = Mage::getModel('personal_orderlist/orderlist')->getCollection()
->addFieldToSelect('product_id')
->addFieldToFilter('b.item_nr', null)
->getSelect()
->joinLeft( array('b'=>$this->getTable('catalog/product')), 'main_table.product_id = b.item_nr', array('b.item_nr'));
echo $orderlistCollect;
}
By echoing the variable, I get the following query.
SELECT `main_table`.`product_id`, `b`.`erp_item_nr`
FROM `mag1personal_orderlist` AS `main_table`
LEFT JOIN `` AS `b` ON main_table.product_id = b.item_nr
WHERE (b.item_nr = '')
This looks fairly close to what I want, however the big problem is that I have no idea how to retrieve the information I should derive from this query and echo it. I've tried to use a variety of collection manipulation methods to no avail. Is it easy to use them out of order as well?
Any help here is appreciated.
Collections use interfaces from PHP's standard library to implement for each-able behaviors.
If that sounded like greek, give this a try.
foreach($orderlistCollect as $item)
{
$data = $item->getData();
var_dump($data);
$sku = $item->getSku();
var_dump($sku);
$sku = $item->getData('sku');
var_dump($sku);
}
If you're interested in learning how to do with with your own PHP objects, the Object Iteration section of the manual is a good place to start. However, there's no need to dive deep on this — just treat a collection like you would an array of objects, and you'll be fine.

Get all the includes from an Entity Framework Query?

I've the following Entity Model : Employee has a Company and a Company has Employees.
When using the Include statement like below:
var query = context.Employees.Include(e => e.Company);
query.Dump();
All related data is retrieved from the database correctly. (Using LEFT OUTER JOIN on Company table)
The problem is hat when I use the GroupBy() from System.Linq.Dynamic to group by Company.Name, the Employees are missing the Company data because the Include is lost.
Example:
var groupByQuery = query.GroupBy("new (Company.Name as CompanyName)", "it");
groupByQuery.Dump();
Is there a way to easily retrieve the applied Includes on the 'query' as a string collection, so that I can include them in the dynamic GroupBy like this:
var groupByQuery2 = query.GroupBy("new (Company, Company.Name as CompanyName)", "it");
groupByQuery2.Dump();
I thought about using the ToString() functionality to get the SQL Command like this:
string sql = query.ToString();
And then use RegEx to extract all LEFT OUTER JOINS, but probably there is a better solution ?
if you're creating the query in the first place - I'd always opt to save the includes (and add to them if you're making a composite query/filtering).
e.g. instead of returning just 'query' return new QueryContext {Query = query, Includes = ...}
I'd like to see a more elegant solution - but I think that's your best bet.
Otherwise you're looking at expression trees, visitors and all those nice things.
SQL parsing isn't that straight either - as queries are not always that simple (often a combo of things etc.).
e.g. there is a `span' inside the query object (if you traverse a bit) which seems to be holding the 'Includes' but it's not much help.

Resources