Silverstripe 3.1 - Can't create a new many_many relation - php

while extending the CsvBulkUploader to fit my needs, I cam across the problem, that Silverstripe doesn't let me create a new entry for a many_many relation.
My dataobject is ShopItems and has a many_many relation called Visuals. So in my MySQL database I get ShopItems_Visuals.
Now I want to create a new entry for this with the following code, and I think here's the place I made some mistake.
...
$visual = ShopItem_Visuals::create();
$visual->ImageID = $file->ID;
$visual->ShopItemID = $obj->ID;
$visual->write();
...
after adding this to my function, I receive Class 'ShopItem_Visuals' not found after hitting the import button.
Is that because the database Table was created through the many_many relation in ShopItem and has no ClassName itself?
Can someone tell me how to create a new entry for this relation?
Thank you in advance.

I don't think that there's a Class for the mapping table itself.
The entry in it should be created automagically, when adding a related Object via add.
$visual = new Visual();
...
$visual->write();
$ShoptItem->Visuals()->add($visual);
$ShoptItem->write();
If the many-many-relation name is Visuals, calling ->Visuals() should return an instance of ManyManyList on which you can call add, remove etc.
see http://api.silverstripe.org/3.0/class-ManyManyList.html

Related

Laravel Eloquent Save model with newly created related entities

I'm new to Laravel, and trying to save the Car with a specific Wheels as follows;
$wheel1=new Wheel();
$wheel1->save();
$wheel2=new Wheel();
$wheel2->save();
$car= new Car();
$car->name='Mazda';
$car->wheelid_1='1';
$car->wheelid_2='2';
$car->save();
The problem I'm having is, I need to save the car, wheel1, and wheel2 objects at the same time without referring to or knowing their id's. But I have no wheelid_1 and wheelid_2 until I save the wheel1 and wheel2 first.
I referred this, this and other similar questions but was unable to figure out how to assign wheel1 and wheel2 as new related objects to the car model.
I have done similar tasks using Entity Framework, by just assigning child objects to relevant properties of the parent object with C#. Can I use a similar method in Eloquent?
I have added foreign keys for Wheels and Car on the table creation.
How can I make the reference between all these 03 objects without using their ids for saving?
What I can imagine is something like the below;
$wheel1=new Wheel();
$wheel2=new Wheel();
$car= new Car();
$car->name='Mazda';
$car->wheel1=$wheel1;
$car->wheel2=$wheel2;
$car->save();
Any help would be highly appreciated.
When you create a new model of item, in this case wheel. It gives you an id.
$wheel1=new Wheel();
$wheel1->save();
$wheel2=new Wheel();
$wheel2->save();
So you can use the model like this and retrieve the id
$wheel1->id;
$wheel2->id;
$car= new Car();
$car->name='Mazda';
if new car has property wheelid_1 & wheelid_2
you can save the ids of this wheel like so
$car->wheelid_1 = $wheel1->id;
$car->wheelid_2 = $wheel2->id;
$car->save();
A few bewares is that if you create a wheel model like that, make sure that in your migration the columns are set to nullable for no errors.

Doctrine Error saving two records at once (new entity was found through the relationship)

When I want to save 1 record to the database, everything works correctly.
When I want to save 2 records right after each other - an error occurs:
[Doctrine\ORM\ORMInvalidArgumentException]
A new entity was found through the relationship 'App\Entity\User#directory' that was not configured to cascade persist
operations for entity:. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity
or configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}). If you
cannot find out which entity causes the problem implement 'Main\Entity\Directory#__toString()' to get a clue.
I just started learning symfony and can't figure out what the problem is.
In my case: I create a new user who takes certain information from a directory that is already in my database.
For example, my new user is a cat lover, then the entry "loves cats" should pull up from the directory.
The text of the error says that I have to set up a connection and save a new record about the love of cats. But my reference book is already full, it does not need to be supplemented, just take information from there.
When I create one user, everything is fine. When I create two users who are supposed to take information from the directory and save the data, an error occurs.
Help me please.
I tried in different places to use method: $this->em->clear(). But it did not help.
class User:
ManyToOne(targetEntity="App\Entity\Directory")
JoinColumn(nullable=true)
private $directory;
public function createData($data) {
$this->setData($data);
$this->em->flush();
}
public function setData($data) {
$user = new User();
$directory = $this->em->getRepository(Directory::class)->findOneBy(['id' => $data['id']]);
$user->setDirectory($directory);
return $this;
}
You need to configure cascade operations for Directory entity which relates to user. Or you can do $em->persist($directory) before flushing

SQLSTATE[42S02]: Base table or view not found laravel

So, going into the problem straight away. someone told me that we dont need to make a pivot table if we only want to have ids of the table. laravel can itself handle this situation. I dont know how this works. I have a table community and another table idea. relation is like this;
One community can contain many ideas and an idea can be found in many
communities.
Relation in idea Model:
public function community() {
return $this->belongsToMany('App\Community')->withTimestamps();
}
Relation in community Model:
public function idea() {
return $this->belongsToMany('App\idea');
}
Now i want to fetch all the records related to a single community to show on its page Let's say the community is Arts.
Here is Controller function:
public function showCommunities($id) {
$community = Community::findOrFail($id)->community()->get();
return view('publicPages.ideas_in_community', compact('community'));
}
When i attach ->community()->get() to the Community::findOrFail($id) Then it throws the error
SQLSTATE[42S02]: Base table or view not found laravel
Any help would be appreciated.
Edit:
Logically, this piece of code Community::findOrFail($id)->community()->get() should be like this Community::findOrFail($id)->idea()->get(). Now it is true but it has little issue. it throws an error
Fatal error: Class 'App\idea' not found
The way you define the many-to-many relation looks ok - I'd just call them communities() and ideas(), as they'll return a collection of objects, not a single object.
Make sure you use correct class names - I can see you refering to your model classes using different case - see App\Community and App\idea.
In order to find related models, Eloquent will look for matching rows in the pivot table - in your case it should be named community_idea and have 3 fields: community_id, idea_id and autoincrement primary key id.
With that in place, you should be able to get all ideas linked to given community with:
$ideas = Community::findOrFail($communityId)->ideas;
If you need communities linked to given idea, just do:
$communities = Idea::findOrFail($ideaId)->communities;
You can read more about how to use many-to-many relationships here: https://laravel.com/docs/5.1/eloquent-relationships#many-to-many
someone told me that we dont need to make a pivot table if we only want to have ids of the table
The above is not true (unless I've just misunderstood).
For a many-to-many (belongsToMany) their must be the two related table and then an intermediate (pivot) table. The intermediate table will contain the primary key for table 1 and the primary key for table 2.
In laravel, the convention for naming tables is plural for your main tables i.e. Community = 'communities' and Idea = 'ideas'. The pivot table name will be derived from the alphabetical order of the related model names i.e.
community_idea.
Now, if you don't want/can't to follow these conventions that's absolutely fine. For more information you can refer to the documentation: https://laravel.com/docs/5.2/eloquent-relationships#many-to-many
Once you're happy that you have the necessary tables with the necessary fields you can access the relationship by:
$ideas = $community->ideas()->get();
//or
$ideas = $community->ideas;
So you controller would look something like:
public function showCommunities($id)
{
$community = Community::findOrFail($id);
//The below isn't necessary as you're passing the Model to a view
// but it's good for self documentation
$community->load('ideas');
return view('publicPages.ideas_in_community', compact('community'));
}
Alternatively, you could add the ideas to the array of data passed to the view to be a bit more verbose:
public function showCommunities($id)
{
$community = Community::findOrFail($id);
$ideas = $community->ideas
return view('publicPages.ideas_in_community', compact('community', 'ideas));
}
Hope this helps!
UPDATE
I would imagine the reason that you're receiving the App\idea not found is because the model names don't match. It's good practice (and in certain environments essential) to Capitalise you class names so make sure of the following:
Your class name is Idea and it's file is called Idea.php
The class has it's namespace declared i.e. namespace App;
If you've added a new class and it's not being found you might need to run composer dump-autoload from the command line to update the autoloader.

Laravel: Create or update related model?

Please be gentle with me - I'm a Laravel noob.
So currently, I loop through a load of users deciding whether I need to update a related model (UserLocation).
I've got as far as creating a UserLocation if it needs creating, and after a bit of fumbling, I've come up with the following;
$coords = $json->features[0]->geometry->coordinates;
$location = new UserLocation(['lat'=>$coords[1],'lng'=>$coords[0]]);
$user->location()->save($location);
My issue is that one the second time around, the Location may want updating and a row will already exist for that user.
Is this handled automatically, or do I need to do something different?
The code reads like it's creating a new row, so wouldn't handle the case of needing to update it?
Update - solution:
Thanks to Matthew, I've come up with the following solution;
$location = UserLocation::firstOrNew(['user_id'=>$user->id]);
$location->user_id = $user->id;
$location->lat = $coords[1];
$location->lng = $coords[0];
$location->save();
You should reference the Laravel API Docs. I don't think they mention these methods in the "regular docs" though so I understand why you may have not seen it.
You can use the models firstOrNew or firstOrCreate methods.
firstOrNew: Get the first record matching the attributes or instantiate
it.
firstOrCreate: Get the first record matching the attributes or create it.
For Example:
$model = SomeModel::firstOrNew(['model_id' => 4]);
In the above example, if a model with a model_id of 4 isn't found then it creates a new instance of SomeModel. Which you can then manipulate and later ->save(). If it is found, it is returned.
You can also use firstOrCreate, which instead of creating a new Model instance would insert the new model into the table immediately.
So in your instance:
$location = UserLocation::firstOrNew(['lat'=>$coords[1],'lng'=>$coords[0]]);
$location will either contain the existing model from the DB or a new instance with the attributes lat and lng set to $coords[1] and $coords[0] respectively, which you can then save or set more attribute values if needed.
Another example:
$location = UserLocation::firstOrCreate(['lat'=>$coords[1],'lng'=>$coords[0]]);
$location will either contain the existing model from the DB or a new model with the attributes set again, except this time the model will have already been written to the table if not found.

Copy a model to another database in symfony 1.4

Using Symfony 1.4 and doctrine I'd like to save a retrieved model to a different database connection:
retrieve model from master-database
change database connection to slave-database
save the model to the slave-database
I have the 2 connections defined in databases.yml.
here in pseudo-code:
$model = [retrieved from master-database];
$slaveConnection = Doctrine_Manager::getInstance()
->getConnection('slave-connection');
$model->save($slaveConnection);
If I create a new model, $model=new model(); the "code" above successfully saves the model to the slave-connection.
What is going wrong?
According to the Symfony log, Symfony recognizes the model as existing and issues an update (instead of an insert).
UPDATE model SET updated_at = '2011-10-21 17:37:32' WHERE id = '1';
Although Symfony is using the correct database connection ('slave-connection'), the update fails because the model isn't present in the slave-database, yet.
And the insert into the slave-database should use all values of the model, not only the changed ones, too.
Anyone can point me to the right direction to save an existing model to a different database?
edit with my solution.
Thanks samura!
Just some additions:
After performing deep copy Symfony saved a new id. But I wanted to really clone the model object to the slave db and so, I had to modify the id.
That caused unique constraint exceptions, so I had to delete first. So this is it:
$id = $model->getId();
$slaveConnection->execute("delete from modeltable where id=".$id);
$model_copy = $model->copy(true); # deep copy
$model_copy->setId($id);
$model_copy->save($slaveConnection);
hope this helps if someone else stumbles.
You could use the public function copy($deep = false) method of the Doctrine_Record class.
$model = [retrieved from master-database];
$slaveConnection = Doctrine_Manager::getInstance()
->getConnection('slave-connection');
$model_copy = $model->copy(true); # deep copy
$model_copy->save($slaveConnection);

Categories