laravel 5 insert relationship between two tables - php

User hasmany profiles
Profile belongs to user.
Following works:
$u = User::firstOrNew(['email' => $s['email']]);
$u->name = $s['name'];
$u->avatar = $s['avatar'];
$u->save();
$p = new UserProfile;
$p->provider = $s['provider'];
$p->provider_uid = $s['provider_uid'];
if ($u->profiles()->save($p)) {
}
But I don't really like it, is there a better more streamlined way? Why can't I save in 1 atomic insert?

You are trying to save data to 2 different table, that's why you can't do this using a single insert.
The way you do it - first save the parent object, then associate the child object and save that - is how it is usually done.
You could also have a look at the push() method of Eloquent's models, that works in a similar fashion as save() but also calls save() on related models. Using this method allows to replace this code:
$a = new A;
$a->save();
$b = new B;
$b->a()->associate($a);
$b->save();
with
$a = new A;
$b = new B;
$b->a()->associate($a);
$a->push();

Related

PHP, Laravel - Concatenation with String to Model

Is is possible to concatenate a string to Model? The below doesn't seem to work.
I have Car Model and Train Model. Vehicle will be either Car or Train.
$vehicle = 'Car';
$result = $vehicle::where('model', $value)->count() > 0;
The above code exists the following error.
Symfony\Component\Debug\Exception\FatalThrowableError: Class 'Car' not found in file
How can I concatenate? Is it possible?
I think this is the closest to what you need,
you don't have to import anything since you have a dynamic value. (If you have 10 possible models you will have to import all of them)
So you should do something like this
$model = "Car";
$modelsPath = "\\App\\Models\\";
$className = $modelsPath.$model;
// if you need an instance you do this
// $instance = new $className;
$result = $className::where('model', $value)->count() > 0;
dd($result);
What i usually do is create a config file that contains an array of all possibilities where they key is the type (Car,Bus,Train in ur example)
And then i get the value using the key which is the input (car) and the model path will be in the config! that way you can swap it later easy and attach more conditions related to that type of model etc.
I hope this makes sense
Regards
use App\Car; //place this at the top of your page. Make sure the model exists
Yes, you can concatenate a model depending upon the condition. Below, I have added a workable code to implement.
Import both model.
//use App\Car;
//use App\Train;
$isCar = true; // boolean condition to check whether it is a car or train
$classVariable = null;
if ($variable) {
$classVariable = new Car();
}else{
$classVariable = new Train();
}
$result = $classVariable->where('model', $value)->get();
//this will provide you a car model query
dd($result);
Better Approach
A better approach will be to use a Vehicle model and Vehicle Type.
Add a vehicle_type_id in the vehicles table.
So whenever you need to retrieve car types, you can use
//Suppose vehicle_type_id for car is 1
$carVehicle = Vehicle::where('vehicle_type_id', 1)->get();
//Suppose vehicle_type_id for train is 2
$trainVehicle = Vehicle::where('vehicle_type_id', 2)->get();
It's Possible
use App\Car;
use App\Train;
/* Here You can now set Model name dynamically*/
$vehicle = 'Car';
$result = $vehicle::where('model', $value)->count() > 0;
I think one approach can as below:
$vehicle = Car::class; //this will dynamically add full namespace of Car class.
$result = $vehicle::where('model', $value)->count() > 0;

Symfony - clone all values from one object onto another by ID?

I am stuck in a huge mess.
I am trying to write a method that should receive user_id and check if there is a profile change request attached to that user.
If there is (has field change_request set to true in users table), the change request should be applied -> all user data fields from change request table by that id should be moved to that user table.
My service
public function getUserApplyChangeRequest($id)
{
$a =$this->getUserRepository()->find($id);
$b =$this->getChangeProfileRequestRepository()->find($id);
$b = clone $a;
$this->em->persist($b);
$this->em->flush();
}
My controller..
public function userApplyChangeRequestAction($changeRequest)
{
$this->requirePostParams(['user_id']);
if ($changeRequest === 1){
$applyChange = $this->get('user')->getUserApplyChangeRequest($this->getUser());
}
return $this->success();
}
I need help because I am stuck and don't really know what to do wtih this lines of code but I putted an example of what I want to happen.
Easiest would be to set the properties yourself if there are only five:
public function getUserApplyChangeRequest($id)
{
$a =$this->getUserRepository()->find($id);
$b =$this->getChangeProfileRequestRepository()->find($id);
$a->setPropertyOne($b->getPropertyOne());
$a->setPropertyTwp($b->getPropertyTwo());
$this->em->persist($a);
$this->em->flush();
}
Other option is to get all the properties of your Change Object with doctrine and call the getters/setters that way (untested, make sure to add NULL checks and skip the ID field):
$props = $em->getClassMetadata(get_class($b))->getColumnNames();
foreach($props as $prop){
//get value from B
$reflectionMethod = new ReflectionMethod(get_class($b),'get'.ucfirst($prop));
$value = $reflectionMethod->invoke($b);
//set value in A
$reflectionMethod = new ReflectionMethod(get_class($a),'set'.ucfirst($prop));
$reflectionMethod->invoke($a, $value);
}

Symfony2/3: Is flushing a single entity/object possible? If not, how can I access objects persisted but not flushed after a foreach loop?

I know that in Doctrine (as a general rule) it is better to flush() after persisting all the entities/objects to the database, but in the following case I think it could be useful to do the opposite.
Example:
Imagine that you are cycling through a list of sport results like this one:
playerA_unique_tag (string), playerB_unique_tag (string), result
In the database, playerA and playerB are FOREIGN KEYS (that point to a User entity). So, the database structure would be similar to this one:
Match record
id, playerA_fk, playerB_fk, result
User records
id, playerA_unique_tag, (etc... many other fields)
id, playerB_unique_tag, (etc... many other fields)
Example of a script
$sportResultsArray = array();
foreach($sportResultsArray as $sportResult){
$playerA_tag = $sportResult["$playerA_unique_tag"];
$db_playerA = db->getRepository("App:User")->findOneByTag($playerA);
if(!$db_playerA){
$db_playerA = new User();
$db_playerA ->setPlayer_unique_tag($playerA_tag);
$em->persist($db_playerA );
}
$match = new Match();
$match ->setplayerA($db_playerA );
/*Same thing would be done for playerB*/
$em->persist($match );
}
Problem:
Of course playerA will play MULTIPLE matches, and each time I have to somehow retrieve the corresponding User object and pass it to the new Match object.
But how can I do that if I haven't flushed playerA User object yet.
The only two alternatives I can think of are:
1- Flushing the User entity (and ONLY the User entity) after it is created
2- Create a temporary array of objects like this:
array('playerA_unique_tag' => playerA_Object, etc.)
Problem with option_1:
I have tried $em->flush($db_playerA); but every entity that was persisted to the Entity Manager also gets flushed (contrary to what written here: http://www.doctrine-project.org/api/orm/2.5/source-class-Doctrine.ORM.EntityManager.html#338-359). Basically, the result is the same as $em->flush();
Problem with option_2:
Isn't it a bad and inefficient workaround?
Consider to work with in-memory registry of players as following:
// init registry
$players = [];
foreach ($sportResultsArray as $sportResult) {
$players[$sportResult["$playerA_unique_tag"]] = null;
$players[$sportResult["$playerB_unique_tag"]] = null;
}
// fetch all at once
$existing = $db->getRepository("App:User")->findBy(['tag' => array_keys($players)]);
// fill up the registry
foreach ($existing as $player) {
$players[$player->getTag()] = $player;
}
// match them up
foreach ($sportResultsArray as $sportResult) {
$playerA_tag = $sportResult["$playerA_unique_tag"];
if ($players[$playerA_tag] === null) {
$players[$playerA_tag] = new User();
$players[$playerA_tag]->setPlayer_unique_tag($playerA_tag);
$em->persist($players[$playerA_tag]);
}
$match = new Match();
$match->setplayerA($players[$playerA_tag]);
/*Same thing would be done for playerB*/
$em->persist($match);
}
// finally
$em->flush();

Duplicate an AR record & re-insert this into the database

I have a AR model that I am trying to duplicated but just need to manually change the foreign key.
$_POST['competition_id'] = 99;
$prizes = CompetitionPrizes::model()->findAll('competition_id =:competition_id',array(':competition_id'=> $_POST['competition_id']));
This query basically queries the prizes table and gets all the rows for a particular competition. With the prizes object I would like to basically re-insert/duplicate the same information except the competition id which I want to manually set.
I did something similar for an AR object that basically only has one row and that worked well, however in this instance as a competition can have more than one prize this same code won't.
// My existing code for duplication process
$obj = Competitions::model()->find('competition_id=:competition_id', array(':competition_id' => $post['competition_id']));
$clone = clone $obj;
$clone->isNewRecord = true;
unset($clone->competition_id); // i want to remove this so it is auto inserted instead via the db
$clone->save();
This works great - how would I modify this on a 'collection' of prizes and have this duplicated into the database while setting my own 'competition_id' value.
Note - i'm to new to Yii, so please let me know if I have made any obvious errors/bad practice
Cloning won't work. You need to assign the attributes to a new object:
$obj = Competitions::model()->find('competition_id=:competition_id', array(':competition_id' => $post['competition_id']));
$clone = new Competitions;
$clone->attributes = $obj->attributes;
$clone->save();
If a more generic way of duplicating a Model / ActiveRecord in Yii2 Framework is required, you might use this solution:
$copy = clone $model;
$copy->isNewRecord = true;
foreach ($model->getPrimaryKey(true) as $field => $value) {
unset($copy->{$field});
}
$copy->save();
GitHub issue discussion about duplicate models: https://github.com/yiisoft/yii2/issues/7544#issuecomment-77158479
The answer for my problem although Michiel above helped me out - alternatively if you wouldn't mind adding another answer i'll give you the accepted answer.
foreach($models as $model)
{
$clone = new Competitions;
$clone->attributes = $model->attributes;
$clone->competition_id = '123' // custom var i am setting manually.
$clone->save();
}
How about (yii2 syntax):
$model=Competitions::findOne([':competition_id' => $post['competition_id']]);
$model->id = null;
$model->isNewRecord = true;
$model->save();

How Doctrine saves new related objects

If I create a new Doctrine object, with lots of relations, should I save() these relations before assigning them to newly created object?
E.g.
$main = new Main();
$child = new Child();
$main->child_rel = $child; // do I need to save the child obj explicitly?
$main->save();
I assumed that parent will automatically call cascading saves, but this doesn't seem to be the case for a newly instantiated parent object.
How does it really work?
Doctrine take care of everything and save related records if needed.
By the way, you don't need to instanciate an related object. You can use this syntax :
$user->Email->address = 'me#server.com';
$user->save();
In the case of one-to-many and many-to-many relationships :
$user->Phonenumbers[]->phonenumber = '123 123';
$user->Phonenumbers[]->phonenumber = '456 123';
$user->Phonenumbers[]->phonenumber = '123 777';
$user->save();
More infos on the doctrine documentation.

Categories