Can someone explain this snippet of magento code? - php

could some one please explain this snippet of magento code found in loadByCustomerId() in the class Mage_Sales_Model_Mysql4_Quote.
$read = $this->_getReadAdapter();
$select = $this->_getLoadSelect('customer_id', $customerId, $quote)
->where('is_active=1')
->order('updated_at desc')
->limit(1);
$data = $read->fetchRow($select);
When i var_dump($data) i see that its an array of customer data. What is model associated with this $data array? Thanks.

Magento "Models" (meaning the entities that allow you to interact with the database, not general purpose server/domain models) have two layers. The first is the "Model" layer. This contains methods for logical interactive with an model. (get me a customer's address, place the order, etc). The second layer is the "Resource Model" layer. Resource Models handle any interactive with the database (or,more generally, the data-store,or the persistance layer, or etc.).
The way a Resource Model interacts with the database is via adapter objects. One for reading information, another for writing information.
So, you're in the class Mage_Sales_Model_Mysql4_Quote. This is a Resource Model. It's the backend for the Mage_Sales_Model_Quote object, instantiated with
$model = Mage::getModel('sales/quote');
With this line
$read = $this->_getReadAdapter();
you're getting a reference to the model's read adapter. This will let you make queries to the database.
With this line
$select = $this->_getLoadSelect('customer_id', $customerId, $quote)
->where('is_active=1')
->order('updated_at desc')
->limit(1);
You're getting a reference to the SQL statement (also an object) that this Resource Model will use to load a sales/quote object.
//gets a reference
$this->_getLoadSelect('customer_id', $customerId, $quote)
Then, you're calling methods on that object to alter it with additional logic
->where('is_active=1')
->order('updated_at desc')
->limit(1);
In pseudo sql, a query might look like this normally
SELECT * FROM quote_table;
But after you call those methods, the query will look something like
SELECT * FROM quote_table
WHERE is_active = 1
ORDER BY updated_at desc
LIMIT 1;
Finally,
$data = $read->fetchRow($select);
here you're using the read adapter you fetched earlier to make a query into the database for the specific quote item row that your query will fetch.

_getReadAdapter() gets the read-only database connection. _getLoadSelect creates a select query on the model's (Mage_Sales_Model_Mysql4_Quote) main table. The data returned is just raw data from the SQL query not associated with any particular backend model.

Related

laravel eloquent relationship hasmany query

i am trying to implement a sql query
select * from user,comments where comments.user_id= user.id
so i create a getcomments method on my user model with following code
public function comments(){return $this->hasMany('Comments')}
and now am accessing the data by
$data = User::find(1)->comments;
but it gave me the data only from comments table (not user and comments )
how can i do this
The Eloquent ORM follows the Active Record pattern. It is a slightly different way to think about modeling and interacting with your data when you come from writing pure sql statements.
Setting up the comments relationship is a good step. Now you need to think about how you interact with your data.
You can get all the information with the following statement:
$user = User::with('comments')->find(1);
With this statement, all of the user information is loaded into the $user object, and all of the comment information is loaded into the $user->comments Collection attribute. This information can be accessed like so:
// get the info
$user = User::with('comments')->find(1);
// display some user info
echo $user->first_name;
echo $user->last_name;
// loop through the comment Collection
foreach($user->comments as $comment) {
// display some comment info
echo $comment->text;
}
The with('comments') section tells the query to eager load all the comments for the returned users (in this case, just the one with id 1). If you didn't eager load them, they would be lazy loaded automatically when you try to access them. The above code would work exactly the same without the with('comments'). Eager loading becomes more important when your loading multiple parent records, though, instead of just one, as it solves the N+1 problem. You can read about eager loading here.
Caution (the reason I added a new answer):
User::find(1)->with('comments')->get();, as otherwise suggested, is not going to provide the information you're looking for. This will actually end up returning all your users with their comments eager loaded. Here is why:
First, User::find(1) is going to return the one user with an id of 1, which is good. However, it then calls with('comments') on this model, which actually creates a new query builder instance for the users table. Finally, it calls get() on this new query builder instance, and since it doesn't have any constraints on it, it will return all the users in the table, with all the comments attached to those users eager loaded.
You are fetching just comments of the user which you select by id. You should this;
User::find(1)->with('comments')->get();
First find a user. Then access all user data, and all comments of user.
$data = User::find(1);
//or you can use eager loading for more performance. thanks for #Özgür Adem Işıklı
$data = User::with('comments')->find(1);
//access user data
$data->id;
$data->email; //etc.
//user's comments:
foreach($data->comments as $comment) {
//access comment detail
$comment->id;
$comment->title;
}

Eloquent - Updating all models in a collection

I want to set a certain attribute in all the models of a collection.
in plain SQL:
UPDATE table SET att = 'foo' WHERE id in (1,2,3)
the code i have:
$models = MyModel::findMany([1,2,3]);
$models->update(['att'=>'foo']);
taken from here
but doesn't work. I'm getting
Call to undefined method Illuminate\Database\Eloquent\Collection::update()
the only way i have found it's building a query with the query builder but i'd rather avoid that.
You are returning a collection, not keeping the query open to update. Like your example is doing.
$models = MyModel::whereIn('id',[1,2,3]);
$models->update(['att'=>'foo']);
whereIn will query a column in your case id, the second parameter is an array of the ids you want to return, but will not execute the query. The findMany you were using was executing it thus returning a Collection of models.
If you need to get the model to use for something else you can do $collection = $models->get(); and it will return a collection of the models.
If you do not just simply write it on one line like so;
MyModel::whereIn('id',[1,2,3])->update(['att'=>'foo']);
Another option which i do not recommend is using the following;
$models = MyModel::findMany([1,2,3]);
$models->each(function ($item){
$item->update(['att'=>'foo']);
});
This will loop over all the items in the collection and update them individually. But I recommend the whereIn method.
The best solution in one single query is still:
MyModel::whereIn('id',[1,2,3])->update(['att'=>'foo']);
If you already have a collection of models and you want to do a direct update you can use modelKeys() method. Consider that after making this update your $models collection remains outdated and you may need to refresh it:
MyModel::whereIn('id', $models->modelKeys())->update(['att'=>'foo']);
$models = MyModel::findMany($models->modelKeys());
The next example I will not recommend because for every item of your $models collection a new extra query is performed:
$models->each(function ($item) {
$item->update(['att'=>'foo']);
});
or simpler, from Laravel 5.4 you can do $models->each->update(['att'=>'foo']);
However, the last example (and only the last) is good when you want to trigger some model events like saving, saved, updating, updated. Other presented solutions are touching direct the database but models are not waked up.
Just use the following:
MyModel::query()->update([
"att" => "foo"
]);
Be mindful that batch updating models won't fire callback updating and updated events. If you need those to be fired, you have to execute each update separately, for example like so (assuming $models is a collection of models):
$models->each(fn($model) => $model->update(['att'=>'foo']) );

Symfony2 - Show all tables from database

I would like to retrieve all the tables of my database as a list.
i tried to do a "Show databases" on a query but as i'm not using a class I defined (entity) in symfony it's not working.
And with DQL :
$em = $this->getDoctrine()->getEntityManager();
$query = $em->createQuery(
'show databases');
$result = $query->getResult();
This error :
[Syntax Error] line 0, col 0: Error: Expected SELECT, UPDATE or DELETE, got 'show'
Any idea to help me ?
As mentioned in a different answer, you can use Doctrine\DBAL for that:
/** #type \Doctrine\DBAL\Connection $connection */
$connection = ...;
/** #type \Doctrine\DBAL\Schema\MySqlSchemaManager $sm */
$sm = $connection->getSchemaManager();
And then just list the tables as Array:
var_dump( $sm->listDatabases() );
My 2 cents:
getContainer()->get('doctrine.dbal.default_connection')->getSchemaManager()->listTableNames()
This will give you an array of table names.
i have a couple cases where I need to use complex sql statements/functions that I just couldn't do in DQL. Luckily, Symfony2/doctrine provide a method to grab the current database connection and bypass doctrine entirely.
//get connection
$conn = $this->get('database_connection');
//run a query
$users= $conn->fetchAll('select * from users');
Be very careful when using this method, however. Since you are bypassing doctrine, you need to handle any security concerns like SQL injection yourself.
You can add Doctrine DBAL to your project and it will give you the tools you need. You can list databases, tables from a database, columns from a table etc etc
More in Doctrine DBAL documentation: http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/schema-manager.html
Doctrine is a ORM, it is not intended to list all the databases. Beside that, usually for the current user you don't have the right to show all databases in the server, this can prove to be a big security breach.
Basicly, doctrine does not know how to interpret your query, you have to use a native query for this: Doctrine Native Query

How to retrieve an entity with all of its associations using EntityManager in Doctrine2?

I have a simple entity with many-to-many and one-to-many associations. I'm aware of 'Joins' for fetching related associations which is a manual solution for my problem.
How can I fetch an entity with all of its associations using EntityManager in Doctrine2? e.g.:
$this->em
->getRepository('Entities\Patientprofile')
->findOneByuserid('555555557')
->fetchAllAssociations();
from http://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html#temporarily-change-fetch-mode-in-dql
you can set eager fetch mode temporarily:
$query = $em->createQuery("SELECT u FROM MyProject\User u");
$query->setFetchMode("MyProject\User", "address", "EAGER");
$query->execute();
If you want do load dynamically all associations with this fetch mode, you can use the getAssociationMappings() method of the Doctrine\ORM\Mapping\ClassMetadataInfo, passing your entity name as parameter to the constructor of ClassMetadataInfo and then iterate over the returned array as $assoc and call:
$query->setFetchMode("MyProject\User", $assoc, "EAGER");
Doc: ClassMetadataInfo#getAssociationMappings()
Doctrine2 setFetchMode not working with "EAGER"
I tried also to fetch the associating entities "eagerly" using setFetchMode in my query, but the following didn't seem to work:
$query->setFetchMode("MyProject\User", "address", "EAGER");
When I jumped into the files I found out that the third parameter $fetchMode should be an integer. The constants are defined in Doctrine\ORM\Mapping:ClassMetadataInfo. When passing a string it will default to Mapping\ClassMetadata::FETCH_LAZY because of this if clause.
/**
* Specifies that an association is to be fetched when it is first accessed.
*/
const FETCH_LAZY = 2;
/**
* Specifies that an association is to be fetched when the owner of the
* association is fetched.
*/
const FETCH_EAGER = 3;
/**
* Specifies that an association is to be fetched lazy (on first access) and that
* commands such as Collection#count, Collection#slice are issued directly against
* the database if the collection is not yet initialized.
*/
const FETCH_EXTRA_LAZY = 4;
So setting the corresponding integer solved the problem:
$query->setFetchMode("MyProject\User", "address", 3);
Or declare the class use Doctrine\ORM\Mapping\ClassMetadata at the top and then use the constant:
$query->setFetchMode("MyProject\User", "address", ClassMetadata::FETCH_EAGER);
EDIT:
Since there seems to be a lot of confusion here on how to fetch associations the right way I will edit my answer and add some additional information on how you can fetch join using your repository.
According to the Doctrine documentation there are 2 types of joins:
Regular Joins: Used to limit the results and/or compute aggregate values.
Fetch Joins: In addition to the uses of regular joins: Used to fetch related entities and include them in the hydrated result of a
query.
So to get an entity including its associations you will need to "fetch-join" all these associations to make sure they are loaded eagerly.
I usually don't use DQL queries for getting entities and solving my fetch joins, instead I add a custom method to a repository where I use a query builder. This is more flexible and much more readable then using DQL. The correct DQL query will be created by the query builder when we call the createQuery method. You can check the created DQL query of course for debug purposes.
An example for such a custom method inside the Patientprofile entity repository from the question above:
public function findPatientByIdWithAssociations($id)(
// create a query builder for patient with alias 'p'
$qb = $this->createQueryBuilder('p')
->where('p.id = :patient_id')
->addSelect('pd')
->leftJoin('p.documentation', 'pd')
->addSelect('pa')
->leftJoin('p.address', 'pa')
->setParameter('patient_id', $id);
$query = $queryBuilder->getQuery();
return $query->getSingleResult();
}
And now you can use your custom repository method to get the patient by id (for example '555555557') including associations to the patient documentation and address:
$repository = $this->em->getRepository('Entities\Patientprofile');
$patient = $repository->findPatientByIdWithAssociations('555555557');
Make sure you use both addSelect and leftJoin to do eager loading.
Doctrine 2 uses Proxy classes for lazy loading, so you don't actually need to have the associations' data fetched until you use the objects. Since the Proxy classes inherit from your association classes, you're able to use the proxies exactly as you would use the fretch association classes.
but, if you really need to fetch the actual association classes, you need to tell the query to set the fetch mode to Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER. If you're using the annotations, you can achieve this with:
e.g.
/**
* #ManyToMany(targetEntity="Item", fetch="EAGER")
*/
private $items;
You can use a DQL query:
$query = $em->createQuery("SELECT p, f FROM Entities\\Patientprofile p JOIN p.Foo f WHERE p.id = ?1");
$query->setParameter(1, 321);
$patient = $query->getSingleResult();
Faced the same problem.
It was necessary to pull out all chain of parents of an element.
$query->setFetchMode(EntityClass, "alias_in_entity", 3) gets only 1 lvl deep, other parents are just proxy.
This can be fixed by changed in entity class fetch mode to eager. But if it`s not if this is not possible for some reason (performance etc), this can be made as #wormhit mentioned by changing entity metadata "on fly"
Example:
$query = $this->entityManager->createQueryBuilder()->select('fields')
->from(FormField::class, 'fields');
$metadata = $this->entityManager->getClassMetadata(FormField::class);
$metadata->setAssociationOverride('parent', ['fetch' => \Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER]);
return $query->getOneOrNullResult();

Zend Dd Rowset getting parent row

I am wondering about using the abstraction in Zend Db RowSet instead of joins, is it possible
for instance I am able to get some info from parent table as in here
/**
* Get default photo info (path , description)
*/
public function getDefaultPhotoInfo($userdId){
$select = $this->select($this)
->where('id=?', $userdId);
$rowset = $this->fetchAll($select);
$current = $rowset->current();
$res = $current->findParentRow('UserPhotos', 'Avatar');
if(isset($res)){
return $res->toArray();
}
}
How can I use Rowset abstraction to get this logic working
table( id, pic_path,) table_translation(id, table_id, lang_id, pic_title);
the above is representation of two tables , the idea is to get the info from both table specifying the lang_id , it is easy with joins but can I do it with the abstraction of Db Rowset ?
Just for clarification: when doing fetchAll on a Zend_Db_Table instance, you get a Zend_Db_Table_Rowset, which implements the Iterator interface. Thus, calling current() on the Rowset instance, will return a Zend_Db_Table_Row instance.
As of ZF1.10, you define relationships between tables in a Zend_Db_Table_Defintion instance or on a concrete table instance like described in the reference guide for Zend_Db_Table Relationships. Since the guide is rather detailed, I won't reproduce this here.
Once you defined relationships, you can fetch them from a row with (example 1 from guide)
$accountsTable = new Accounts();
$accountsRowset = $accountsTable->find(1234);
$user1234 = $accountsRowset->current();
$bugsReportedByUser = $user1234->findDependentRowset('Bugs');
or by the magic finder methods.
The findParentRow() method is somewhat different to that, as it return the full row of a dependent rowset from it's parent row.
Ex5: This example shows getting a Row object from the table Bugs (for example one of those bugs with status 'NEW'), and finding the row in the Accounts table for the user who reported the bug.
$bugsTable = new Bugs();
$bugsRowset = $bugsTable->fetchAll(array('bug_status = ?' => 'NEW'));
$bug1 = $bugsRowset->current();
$reporter = $bug1->findParentRow('Accounts');
When using table relations, keep in mind that these will result in one additional query per fetched dependent table, whereas a Join does it all in one.
Also see this related questions:
Modeling objects with multiple table relationships in Zend Framework

Categories