Using Doctrine PHP
If I have a user with a many to many relationship with the model address and each address has a foreign key to a address type (home, office). Doctrine doesn't automatically load the related records for that address type.
$user = Doctrine::getTable('User')->findOneById(1); // bob
echo $user->Address[0]->address_type_id; // 4
echo isset($user->Address[0]->AddressType); // false
$user->Address[0]->refreshRelated(); // or $user->Address[0]->loadReference('AddressType');
echo isset($user->Address[0]->AddressType); // true
echo $user->Address[0]->AddressType->name; // office
Not sure if this is a bug or not in doctrine or my model.
But is this the best way to load related models beyond one level deep or is there another way to achieve the same result?
Have you simply tried joining you relations one by one?
Works pretty well, if you relations are set up correct.
$user = Doctrine::getTable('User')
->createQuery('u')
->leftJoin('u.Address a')
->leftJoin('a.AddressType t')
->findOneById(1);
You also spare your db 2 sql queries, compared to your example.
Are you saying you can't do this:
echo $user->Address[0]->AddressType->name;
If you try that without the isset, Doctrine should check to see that the value is set before retrieving it for you automatically.
Related
I assume that this should all be in one query in order to prevent duplicate data in the database. Is this correct?
How do I simplify this code into one Eloquent query?
$user = User::where( 'id', '=', $otherID )->first();
if( $user != null )
{
if( $user->requestReceived() )
accept_friend( $otherID );
else if( !$user->requestSent() )
{
$friend = new Friend;
$friend->user_1= $myID;
$friend->user_2 = $otherID;
$friend->accepted = 0;
$friend->save();
}
}
I assume that this should all be in one query in order to prevent
duplicate data in the database. Is this correct?
It's not correct. You prevent duplication by placing unique constraints on database level.
There's literally nothing you can do in php or any other language for that matter, that will prevent duplicates, if you don't have unique keys on your table(s). That's a simple fact, and if anyone tells you anything different - that person is blatantly wrong. I can explain why, but the explanation would be a lengthy one so I'll skip it.
Your code should be quite simple - just insert the data. Since it's not exactly clear how uniqueness is handled (it appears to be user_2, accepted, but there's an edge case), without a bit more data form you - it's not possible to suggest a complete solution.
You can always disregard what I wrote and try to go with suggested solutions, but they will fail miserably and you'll end up with duplicates.
I would say if there is a relationship between User and Friend you can simply employ Laravel's model relationship, such as:
$status = User::find($id)->friends()->updateOrCreate(['user_id' => $id], $attributes_to_update));
Thats what I would do to ensure that the new data is updated or a new one is created.
PS: I have used updateOrCreate() on Laravel 5.2.* only. And also it would be nice to actually do some check on user existence before updating else some errors might be thrown for null.
UPDATE
I'm not sure what to do. Could you explain a bit more what I should do? What about $attributes_to_update ?
Okay. Depending on what fields in the friends table marks the two friends, now using your example user_1 and user_2. By the example I gave, the $attributes_to_update would be (assuming otherID is the new friend's id):
$attributes_to_update = ['user_2' => otherID, 'accepted' => 0 ];
If your relationship between User and Friend is set properly, then the user_1 would already included in the insertion.
Furthermore,on this updateOrCreate function:
updateOrCreate($attributes_to_check, $attributes_to_update);
$attributes_to_check would mean those fields you want to check if they already exists before you create/update new one so if I want to ensure, the check is made when accepted is 0 then I can pass both say `['user_1' => 1, 'accepted' => 0]
Hope this is clearer now.
I'm assuming "friends" here represents a many-to-many relation between users. Apparently friend requests from one user (myID) to another (otherId).
You can represent that with Eloquent as:
class User extends Model
{
//...
public function friends()
{
return $this->belongsToMany(User::class, 'friends', 'myId', 'otherId')->withPivot('accepted');
}
}
That is, no need for Friend model.
Then, I think this is equivalent to what you want to accomplish (if not, please update with clarification):
$me = User::find($myId);
$me->friends()->syncWithoutDetaching([$otherId => ['accepted' => 0]]);
(accepted 0 or 1, according to your business logic).
This sync method prevents duplicate inserts, and updates or creates any row for the given pair of "myId - otherId". You can set any number of additional fields in the pivot table with this method.
However, I agree with #Mjh about setting unique constraints at database level as well.
For this kind of issue, First of all, you have to enjoy the code and database if you are working in laravel. For this first you create realtionship between both table friend and user in database as well as in Models . Also you have to use unique in database .
$data= array('accepted' => 0);
User::find($otherID)->friends()->updateOrCreate(['user_id', $otherID], $data));
This is query you can work with this . Also you can pass multiple condition here. Thanks
You can use firstOrCreate/ firstOrNew methods (https://laravel.com/docs/5.3/eloquent)
Example (from docs) :
// Retrieve the flight by the attributes, or create it if it doesn't exist...
$flight = App\Flight::firstOrCreate(['name' => 'Flight 10']);
// Retrieve the flight by the attributes, or instantiate a new instance...
$flight = App\Flight::firstOrNew(['name' => 'Flight 10']);
use `firstOrCreate' it will do same as you did manually.
Definition of FirstOrCreate copied from the Laravel Manual.
The firstOrCreate method will attempt to locate a database record using the given column / value pairs. If the model can not be found in the database, a record will be inserted with the given attributes.
So according to that you should try :
$user = User::where( 'id', '=', $otherID )->first();
$friend=Friend::firstOrCreate(['user_id' => $myId], ['user_2' => $otherId]);
It will check with both IDs if not exists then create record in friends table.
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;
}
i'm trying to do a complex query with Criteria in a Symfony project using Propel ORM.
the query i want to make is, in human words:
Select from the 'interface' table the registers that:
- 1 are associated with a process (with a link table)
- 2 have a name similat to $name
- 3 its destiny application's name is $apd (application accecible by foreign key)
- 4 its originapplication's name is $apo (application accecible by foreign key)
here the code i made, and not working:
$c = new Criteria();
$c->addJoin($linkPeer::CODIGO_INTERFASE,$intPeer::CODIGO_INTERFASE); //1
$c->add($linkPeer::CODIGO_PROCESONEGOCIO,$this->getCodigoProcesonegocio());//1
if($name){
$name = '%'.$name.'%'; //2
$c->add($intPeer::NOMBRE_INTERFASE,$name,Criteria::LIKE); //2
}
if($apd){
$apd = '%'.$apd.'%'; //3
$c->addJoin($appPeer::CODIGO_APLICACION,$intPeer::CODIGO_APLICACION_DESTINO);//3
$c->add($appPeer::NOMBRE_APLICACION,$apd,Criteria::LIKE); //3
}
if($apo){
$apo = '%'.$apo.'%';//4
$c->addJoin($appPeer::CODIGO_APLICACION,$intPeer::CODIGO_APLICACION_ORIGEN);//4
$c->add($appPeer::NOMBRE_APLICACION,$apo,Criteria::LIKE);//4
}
After that i did a $c->toString() to see the SQL generated and i saw that when i send only an $apd value, the SQL is correct, when i send an $apo value too. But when i send both, only the $apo AND apears on the SQL.
I guess its because the $c->add(...) call is the same with a distinct parameter, but not sure at all. Is this the error? What is the best way to generate my query correctly?
Thank you very much for your time! :D
Yes, it's overriding the previous call as the Criteria object only stores one condition per field. The solution is to create 2 or more separate Criterion objects and mix them into the Criteria object:
//something like this
$cron1 = $criteria->getNewCriterion();
$cron1->add($appPeer::NOMBRE_APLICACION,$apo,Criteria::LIKE);//4
$criteria->add($cron);
//and the same with the other criterion
However, it would be much easier to upgrade to Propel15+, where you work on the Query class level, and multiple restrictions on the same field don't override each other.
Hope this helps,
Daniel
In order to improve performance of app, I would like to separate queries instead of using leftJoins. Then I have to create my own related Doctrine_Collection :
$user->Friends->add($current_friend);
But I don't want doctrine does a query when I try to access related (not loaded) Collection.
How I can do that.
Thanks in advance.
I think the answer is in this § about relation handling. Build a new friendship relation and save it instead of adding a friend to a user object.
then I found this way (I should optimize this) :
$my_relation_collFriend = FriendTable::getInstance()->findByIdUser($user->id_user);
foreach($my_relation_collFriend as $friend)
{
$collFriend = $user->get('Friends', false); //get the related collection without db query
if(!$collFriend ) //unfornatly, It can be null
{
$collFriend = new Doctrine_Collection::create('friend'); //create the collection
$user->set('Friends', $collFriend, false); // define the related collection without db query
}
$collFriend->add($friend); //add the record to related collection
}
With this example I know this is useless but with lot of joins and datas it becomes necessary
I'm using Zend Framework with Doctrine. I'm creating an object, editing, then saving it. That works fine. However, when I later try to find that object based on one of the column values, Doctrine throws an error saying, "Message: Invalid field name to find by:". Notice there is no field name listed in the error message after the :.
My database table does have a column called status and the model base class does know about it. I'm using base classes and table classes in my setup.
Here is my code. The first section works fine and the record gets created in the database. Its the second line of the second section where the error gets thrown. I've tried different variations of the findBy calls, findBy('status', 'test1'), findByStatus('test1'), etc.
$credit = new Model_Credit();
$credit['buyer_id'] = 1;
$credit['status'] = 'test1';
$credit->save();
$creditTable = Doctrine_Core::getTable('Model_Buyer');
$credit = $creditTable->findOneByStatus('test1'); // dying here
$credit['status'] = 'test2';
$credit->save();
Never mind! I hate when you see the answer right after posting a big long question. In the second section I referred to a different model (Model_Buyer) instead of Model_Credit.