How to load models without related objects? - typo3-7.6.x

I fetch a list of model objects from a repository object, with usual code like this:
...
$query = $this->createQuery();
$query->getQuerySettings()->setStoragePageIds($projectStoragePids);
...
$query->matching($query->logicalOr($constraints));
$result = $query->execute();
Now if I debug the objects, I see that all the related complex objects member also have been loaded, and their related ones too and so on. So the repository is delivering a huge tree, while I just need the very list of the objects with no depth. Complex object members could be just NULL. Therefore building the list is too slow and unnecessary.
I am aware of #lazy, however it does not change if I set this attribute in the model (and clear the cache).
What options are there to disable loading related objects in depth besides #lazy? What automatism causes TYPO3 Domain handler to load or skip related objects?
In C#, you can choose by .include(). What about TYPO3?

One important thing is that you can use $query->statement($sql, $params).
It is amongst the extbased query methods:
http://lbrmedia.net/codebase/Eintrag/extbase-query-methods/
If you use this, you can write a select statement that includes only a selection of fields. The T3 model object builder will take the rows and fields you have selected, and will create model objects from the information available.
So for instance, assume we write first:
...
$query = $this->createQuery();
$result = $query->execute();
return $result;
Like this you will get a list of objects with each object member field also initialized with an complete object.
But if we write for instance:
$query = $this->createQuery();
$query->statement('select uid from tx_my_table'); // <<<<<<<<
$result = $query->execute();
return $result;
Then you will still get a list of objects, but the only member field that has a real value is uid. Like this, you can also select a field that points to some m:m relationship. It will be handled.
This may bring a huge gain of speed.
The caveat is that the T3 extbase repository somehow, slightly creates a mixture between your pure sql and some internal specifications. I figure some more hints might be found by the search 'TYPO3 convertQueryToDoctrineQueryBuilder'. Further, you must rewrite your own query restrictions that are usually given by the $querySettings. But hey, it's an open door to optimize.

Try $query->execute(1);. This is the rawResult flag which returns raw-database row as an array instead of an object. Relationships to other objects are not resolved either.

Related

How to create a GraphQL query that returns data from multiple tables/models within one field using Laravel Lighthouse

Im trying to learn GraphQL with Laravel & Lighthouse and have a question Im hoping someone can help me with. I have the following five database tables which are also defined in my Laravel models:
users
books
user_books
book_series
book_copies
I'd like to create a GraphQL endpoint that allows me to get back an array of users and the books they own, where I can pull data from multiple tables into one subfield called "books" like so:
query {
users {
name
books {
title
issue_number
condition
user_notes
}
}
}
To accomplish this in SQL is easy using joins like this:
$users = User::all();
foreach ($users as $user) {
$user['books'] = DB::select('SELECT
book_series.title,
book.issue_number
book_copies.condition,
user_books.notes as user_notes
FROM user_books
JOIN book_copies ON user_books.book_copy_id = book_copies.id
JOIN books ON book_copies.book_id = books.id
JOIN book_series ON books.series_id = book_series.id
WHERE user_books.user_id = ?',[$user['id']])->get();
}
How would I model this in my GraphQL schema file when the object type for "books" is a mashup of properties from four other object types (Book, UserBook, BookCopy, and BookSeries)?
Edit: I was able to get all the data I need by doing a query that looks like this:
users {
name
userBooks {
user_notes
bookCopy {
condition
book {
issue_number
series {
title
}
}
}
}
}
However, as you can see, the data is separated into multiple child objects and is not as ideal as getting it all in one flat "books" object. If anyone knows how I might accomplish getting all the data back in one flat object, Id love to know.
I also noticed that the field names for the relationships need to match up exactly with my controller method names within each model, which are camelCase as per Laravel naming conventions. Except for my other fields are matching the database column names which are lower_underscore. This is a slight nitpick.
Ok, after you edited your question, I will write the answer here, to answer your new questions.
However, as you can see, the data is separated into multiple child objects and is not as ideal as getting it all in one flat "books" object. If anyone knows how I might accomplish getting all the data back in one flat object, Id love to know.
The thing is, that this kind of fetching data is a central idea of GraphQL. You have some types, and these types may have some relations to each other. So you are able to fetch any relations of object, in any depth, even circular.
Lighthouse gives you out of the box support to eloquent relations with batch loading, avoiding the N+1 performance problem.
You also have to keep in mind - every field (literally, EVERY field) in your GraphQL definition is resolved on server. So there is a resolve function for each of the fields. So you are free to write your own resolver for particular fields.
You actually can define a type in your GraphQL, that fits your initial expectation. Then you can define a root Query field e.g. fetchUsers, and create you custom field resolver. You can read in the docs, how it works and how to implement this: https://lighthouse-php.com/5.2/the-basics/fields.html#hello-world
In this field resolver you are able to make your own data fetching, even without using any Laravel/Eloquent API. One thing you have to take care of - return a correct data type with the same structure as your return type in GraphQL for this field.
So to sum up - you have the option to do this. But in my opinion, you have to write more own code, cover it with tests on you own, which turns out in more work for you. I think it is simpler to use build-in directives, like #find, #paginate, #all in combination with relations-directives, which all covered with tests, and don't care about implementation.
I also noticed that the field names for the relationships need to match up exactly with my controller method names within each model, which are camelCase as per Laravel naming conventions.
You probably means methods within Model class, not controller.
Lighthouse provides a #rename directive, which you can use to define different name in GraphQL for your attributes. For the relation directives you can pass an relation parameter, which will be used to fetch the data. so for your example you can use something like this:
type User {
#...
user_books: [Book!]! #hasMany(relation: "userBooks")
}
But in our project we decided to use snak_case also for relations, to keep GraphQL clean with consistent naming convention and less effort

From within a Doctrine record class, querying for the same record from the DB overwrites the properties within that class

In one of my Doctrine record classes, I have a preSave method that performs a check. In this check, a query is done on the same table that my record belongs to. This query will fetch one record from the table, and I use the hydrated result to compare to the current record represented by the class.
In some cases, the fetched hydrated result will be the same record as the one I'm working with in the preSave check. However, when this happens, any changes that I've made to the first record are reverted once the query is completed.
Why does this happen? Is there a workaround?
Doctrine might be maintaining a single reference to the record object instance, and not creating a whole new instance in your preSave() method. So when the object is hydrated, any other variables of the same type in your code are 'refreshed'.
To verify this, inspect the object ID's of variables in your code using spl_object_hash() function.
Without seeing your code, workaround suggestions can vary, but one possible workaround is to hydrate an array in preSave():
$query = Doctrine_Query::create()
->select('foo')
->from('Bar b')
->where('b.id = ?', $id);
$results = $query->execute(array(), Doctrine::HYDRATE_ARRAY);
You will lose the ability to use the result as an object, but you will be able to use the contents of the array for comparisons.

code igniter datamapper join get()

I am using this join in my code igniter model
$this->db->select('e.name, p.projects');
$this->db->from('example as e');
$this->db->join('procure as p', e.id = p.id');
$this->db->where('e.cityid', '1');
$this->db->where('e.status', '0');
I do not have separate table for join. Here is my data mapper, this is not giving any output.
I have two tables and I want to write a join query on them.I have this in my controller.
$example = new Example();
$example ->where_join_field('procure', FALSE);
Update
can you show me the snippet for joining three tables using data mapper.
Generally you don't do joins by hand with DMZ models (the sql generated will use joins nonetheless). The thing you are looking for is Relations.
You have to set up your model's relations, using the $has_one and $has_many attributes, keeping the naming conventions, creating the necessary tables for many-to-many and so on. You can read about these in the docs here and here.
Once you got your models set up, you can use the where_related and include_related methods where you have used joins before:
where_related is for when you want to filter the models you are querying on some related model's field values. So if you have a related project set up on your Example class, you can write ->where_related('project', 'procure', false); and it will filter the returned Example instances based on their related project's procedure field. So basically it's the same conditionals that you would put into the where SQL clause.
include_related is for when you want to include fields from related models or even whole instance. So if you write ->include_related('project', 'projects') when you query Example instances you will end up with a project_projects attribute on the returned instances. There are many options to control how these attributes should be created. Basically these are the fields you would have put into the select SQL clause.
There are magic methods created for every named relation and many other options, i refer you to the Get (Advanced) page of the documentation for start and free to explore the other pages describing relations.

Magento - Custom model one to many relationship

I'm writing a custom module for Magento and I need to implement a one-to-many relationship between two tables.
The easyest solution is to create one model for each table and save data separately, but I think this approach has some limitations (I prefer saving data in one single transaction instead of two, I want to load the combined data when I load the collection, ecc).
What is the best way to handle this kind of situation? Is it possible to have one model class that retrieves data from multiple tables?
Thank you
If the 1-n relationship is strictly a data construct rather than an entity construct, then there is no need for a full ORM representation for the adjacent table.
It's often good to find inspiration and examples in the core code, so please refer to the cms/page and cms/block entities, particularly to their resource models. Take the Mage_Cms_Model_Resource_Page::_getLoadSelect() method as an example, as this method is called by the resource model to load data:
protected function _getLoadSelect($field, $value, $object)
{
$select = parent::_getLoadSelect($field, $value, $object);
$storeId = $object->getStoreId();
if ($storeId) {
$select->join(
array('cps' => $this->getTable('cms/page_store')),
$this->getMainTable().'.page_id = `cps`.page_id'
)
->where('is_active=1 AND `cps`.store_id IN (' . Mage_Core_Model_App::ADMIN_STORE_ID . ', ?) ', $storeId)
->order('store_id DESC')
->limit(1);
}
return $select;
}
Note the join, and note that they have one entity table each (cms_page and cms_block) respectively - this is what the ORM expects, by the way - but there are additional tables which contain cms-entity-to-store data. (cms_page_store and cms_block_store). The records in these latter two tables are not represented by their own ORM classes, rather, they are updated, removed, or joined for loads by the cms/page and cms/block models' resource classes.
Again, the deciding factor on whether to handle the SQL through designated ORM classes comes from the business domain - if the records in the 'many' tables represent things which need presentation or complex representation in your business domain, access them through ORM.

Using LINQ with stored procedure that returns multiple instances of the same entity per row

Our development policy dictates that all database accesses are made via stored procedures, and this is creating an issue when using LINQ.
The scenario discussed below has been somewhat simplified, in order to make the explanation easier.
Consider a database that has 2 tables.
Orders (OrderID (PK), InvoiceAddressID (FK), DeliveryAddressID (FK) )
Addresses (AddresID (PK), Street, ZipCode)
The resultset returned by the stored procedure has to rename the address related columns, so that the invoice and delivery addresses are distinct from each other.
OrderID InvAddrID DelAddrID InvStreet DelStreet InvZipCode DelZipCode
1 27 46 Main St Back St abc123 xyz789
This, however, means that LINQ has no idea what to do with these columns in the resultset, as they no longer match the property names in the Address entity.
The frustrating thing about this is that there seems to be no way to define which resultset columns map to which Entity properties, even though it is possible (to a certain extent) to map entity properties to stored procedure parameters for the insert/update operations.
Has anybody else had the same issue?
I'd imagine that this would be a relatively common scenarios, from a schema point of view, but the stored procedure seems to be the key factor here.
Have you considered creating a view like the below for the stored procedure to select from? It would add complexity, but allow LINQ to see the Entity the way you wanted.
Create view OrderAddress as
Select o.OrderID
,i.AddressID as InvID
,d.AddressID as DelID
...
from Orders o
left join Addresses i
on o.InvAddressID= i.AddressID
left join Addresses d
on o.DelAddressID = i.AddressID
LINQ is a bit fussy about querying data; it wants the schema to match. I suspect you're going to have to bring that back into an automatically generated type, and do the mapping to you entity type afterwards in LINQ to objects (i.e. after AsEnumerable() or similar) - as it doesn't like you creating instances of the mapped entities manually inside a query.
Actually, I would recommend challenging the requirement in one respect: rather than SPs, consider using UDFs to query data; they work similarly in terms of being owned by the database, but they are composable at the server (paging, sorting, joinable, etc).
(this bit a bit random - take with a pinch of salt)
UDFs can be associated with entity types if the schema matches, so another option (I haven't tried it) would be to have a GetAddress(id) udf, and a "main" udf, and join them:
var qry = from row in ctx.MainUdf(id)
select new {
Order = ctx.GetOrder(row.OrderId),
InvoiceAddress = ctx.GetAddress(row.InvoiceAddressId),
DeliveryAddress = ctx.GetAddress(row.DeliveryAddressId)) };
(where the udf just returns the ids - actually, you might have the join to the other udfs, making it even worse).
or something - might be too messy for serious consideration, though.
If you know exactly what columns your result set will include, you should be able to create a new entity type that has properties for each column in the result set. Rather than trying to pack the data into an Order, for example, you can pack it into an OrderWithAddresses, which has exactly the structure your stored procedure would expect. If you're using LINQ to Entities, you should even be able to indicate in your .edmx file that an OrderWithAddresses is an Order with two additional properties. In LINQ to SQL you will have to specify all of the columns as if it were an entirely unrelated data type.
If your columns get generated dynamically by the stored procedure, you will need to try a different approach: Create a new stored procedure that only pulls data from the Orders table, and one that only pulls data from the addresses table. Set up your LINQ mapping to use these stored procedures instead. (Of course, the only reason you're using stored procs is to comply with your company policy). Then, use LINQ to join these data. It should be only slightly less efficient, but it will more appropriately reflect the actual structure of your data, which I think is better programming practice.
I think I understand what you're after, but I could wildy off...
If you mock up classes in a DBML (right-click -> new -> class) that are the same structure as your source tables, you could simply create new objects based on what is read from the stored procedure. Using LINQ to objects, you could still query your selection. It's more code, but it's not that hard to do. For example, mock up your DBML like this:
Pay attention to the associations http://geeksharp.com/screens/orders-dbml.png
Make sure you pay attention to the associations I added. You can expand "Parent Property" and change the name of those associations to "InvoiceAddress" and "DeliveryAddress." I also changed the child property names to "InvoiceOrders" and "DeliveryOrders" respectively. Notice the stored procedure up top called "usp_GetOrders." Now, with a bit of code, you can map the columns manually. I know it's not ideal, especially if the stored proc doesn't expose every member of each table, but it can get you close:
public List<Order> GetOrders()
{
// our DBML classes
List<Order> dbOrders = new List<Order>();
using (OrderSystemDataContext db = new OrderSystemDataContext())
{
// call stored proc
var spOrders = db.usp_GetOrders();
foreach (var spOrder in spOrders)
{
Order ord = new Order();
Address invAddr = new Address();
Address delAddr = new Address();
// set all the properties
ord.OrderID = spOrder.OrderID;
// add the invoice address
invAddr.AddressID = spOrder.InvAddrID;
invAddr.Street = spOrder.InvStreet;
invAddr.ZipCode = spOrder.InvZipCode;
ord.InvoiceAddress = invAddr;
// add the delivery address
delAddr.AddressID = spOrder.DelAddrID;
delAddr.Street = spOrder.DelStreet;
delAddr.ZipCode = spOrder.DelZipCode;
ord.DeliveryAddress = delAddr;
// add to the collection
dbOrders.Add(ord);
}
}
// at this point I have a List of orders I can query...
return dbOrders;
}
Again, I realize this seems cumbersome, but I think the end result is worth a few extra lines of code.
this it isn't very efficient at all, but if all else fails, you could try making two procedure calls from the application one to get the invoice address and then another one to get the delivery address.

Resources