Symfony2, Doctrine, Updation OneToMany entries - symfony

i have function
private function updateCharacters($oApiKeyInfo) // argument is base entity
{
$aXmlCharacter = $this->fXml->getXmlRowset($this->oXml, 'row');
// insert new
foreach ($aXmlCharacter as $oXmlCharacter)
{
$loopData = $this->fXml->getXmlAttributesAsArray($oXmlCharacter);
$oApiKeyInfoCharacters = new apiKeyInfoCharacters();
$oApiKeyInfoCharacters
->setKeyID($this->keyID)
->setCharacterID($loopData['characterID'])
->setCharacterName($loopData['characterName'])
->setCorporationID($loopData['corporationID'])
->setCorporationName($loopData['corporationName'])
->set_apiKeyInfo_byKeyID($oApiKeyInfo);
$this->em->persist($oApiKeyInfoCharacters);
}
// $this->em->flush() is in main (public) function
}
but, it creates dublicates... and i want that in db was ONLY that entries that is in $aXmlCharacter (array), others must be deleted
now code above is only adding new entries, but i need remove previous
can someone help to deal with it? and please show working examples

Dublicate entries i can not see any definition for uniquenes.
Deleting an Object vom database is simple as the documentation shows.
$product = $em->getRepository('YourBundle::apiKeyInfoCharacters')->find($id);
$em->remove($product);
$em->flush();
But why do you want to delete the existing one instead of updating?

Related

Add data when running Symfony migrations

I have a Symfony project that is using the DoctrineMigrations bundle, and I have a really simple question: When I run a migration (e.g., when I'm pushing an update to production), how can I insert data to the database?
For example: I have an Entity which is the type of an add. The entity is:
private $addType; // String
private $type1; // Boolean
private $type2; // Boolean
private $type3; // Boolean
I add another field ($type4), and I want to add a new record to the database, with this values:
$addType = 'Type number 4';
$type1 = false;
$type2 = false;
$type3 = false;
$type4 = true;
How can this be done with DoctrineMigrations? Is it possible?
Using the Entity Manager as suggested in another answer is not a good idea, as it leads to troubles later.
In the first migration, I created a table with users and populated some users via $em->persist($user); which seemed fine at the beginning.
But after a month, I added a phone column to my User model. And Doctrine generates INSERT statements with this column within the first migration, which fails due to the non-existing column phone. Of course it doesn't exist yet in the first migration. So it is better to go with pure SQL INSERTs.
I just asked a related related question.
It is possible to use the migrations bundle to add data to the database. If you add a new property and use the doctrine mapping then the
php app/console doctrine:migrations:diff
command will generate a new migration file. You can just put your insert statements inside this file using the syntax:
$this->addSql('INSERT INTO your_table (name) VALUES ("foo")');
Make sure you put it after the auto-generated schema changes though. If you want to separate your schema changes and your data changes then you can use
php app/console doctrine:migrations:generate
to create an empty migrations file to put your insert statements in.
Like I said in my related question, this is one way to do it, but it requires manually creating these if you want to change this data in the database.
Edit:
Since this answer seems to get a few views I think it's worth adding that to more clearly separate the data changes from the schema changes there is a postUp method that can be overridden and that will be called after the up method.
https://www.doctrine-project.org/projects/doctrine-migrations/en/3.0/reference/migration-classes.html#postup
I've "found" the correct way to solve my problem (insert data after running migrations, using my entity classes).
Here is: https://stackoverflow.com/a/25960400
The idea is to declare the migration as ContainerAware, and then, from the postUp function, call the DI to get the EntityManager. It's really easy, and you can use all your entities and repositories.
// ...
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class Version20130326212938 extends AbstractMigration implements ContainerAwareInterface
{
private $container;
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
public function up(Schema $schema)
{
// ... migration content
}
public function postUp(Schema $schema)
{
$em = $this->container->get('doctrine.orm.entity_manager');
// ... update the entities
}
}
when you make the new field you need to enter this annotation "options={"default":1}" and it should work.
/**
* #var boolean
* #ORM\Column(name="type4", type="boolean", options={"default":1})
*/
private $type4 = true;
Took me some time to figure this out :)
It does, if you know how to format the array;
$this->connection->insert('user', ['id' => 1, 'gender' => 'Male']);
this is good solution for me. Just use bin/console make:migration and when migration is generated just edit if and add "DEFAULT TRUE":
$this->addSql('ALTER TABLE event ADD active TINYINT(1) NOT NULL DEFAULT TRUE');
It doesn't sound a good idea to fill date in migration, not its responsibility, symfony has a way of doing that. https://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html

Symfony 2 Variable Entity Naming

I feel like this shouldn't be to hard, but try as I might, I keep getting errors.
What I'm wanting to do, is to have a single "add" function that will handle the basic functionality of adding records to any / all tables. Basically, the post data will contain the table name, as well as the fields / values to be inserted. The controller itself, confirms the user has access to do these things, and then verifies the fields are valid, before creating a new instance of the entity, that's where things go haywire:
$entityName = 'Products';
$row = new $entityName(); //doesn't work
$row new Products(); //works
I haven't found a way or any examples of creating a new entity using the Entity Manager, or else that might work, because i've created functions using EM to do queries, updates, and deletes, but I just can't get this to work with the add functions.
1. Your problem is almost certainly namespacing (see below). Try this instead:
$entityName = 'My\Bundle\Entity\Products';
$row = new $entityName();
Should work. ;)
2. If you want to create new instances using an EntityManager, try this:
$entityName = 'My\Bundle\Entity\Products';
$row = $em->getClassMetadata($entityName)->newInstance();
...assuming $em is your EntityManager. :-)
3. To "prove" that your problem is namespacing, try this in a plain .php file that you run from the command line:
namespace Foo;
class Test {}
$class1 = 'Foo\Test';
$class2 = 'Test';
$x = new Test(); // Will work
$y = new $class1(); // Will work
$z = new $class2(); // Will trigger "Class 'Test' not found" fatal error
Two things:
Try with "Products" instead of 'Products'
I suppose that your entity Products has a namespace, it is required (even if you declared an use statement). So try with "My\Bundle\Entity\Products".

Create new object from existent object in DB using Symfony2 and Doctrine2

I need to create a new set of DB records by using same data that already exists. See image below:
Using this as example I need to create the same set of records but just changing the column company, less take UPC = I5TYF1UPORMYUY4JT21Z, using this info I should be able to generate two rows by just changing the company to 52 (any number). What I think is to get those records using this:
$entityStockDetailHasProductDetail = $this->getDoctrine()->getManager()->getRepository('ProductBundle:StockDetailHasProductDetail')->findBy(array(
"product" => $request->request->get('product')['id'],
"upc" => $request->request->get('product')['upc'],
));
Which returns the right records but I don't know how to continue from that point. My entity has all this methods:
$entityStockDetailHasProductDetail->setProductDetail();
$entityStockDetailHasProductDetail->setUpc();
$entityStockDetailHasProductDetail->setProduct();
$entityStockDetailHasProductDetail->setCompany();
$entityStockDetailHasProductDetail->setCondition();
$entityStockDetailHasProductDetail->setContent();
Any ideas?
Just loop over the collection, clone your entities, set the new company and persist.
$em = $this->getDoctrine()->getEntityManager('default');
$collection = $em->getRepository('YourBundle:Entity')->findBy(array('...'));
foreach ($collection as $entity) {
$newEntity = clone $entity;
$newEntity
->setId(null)
->setCompany($company)
;
$em->persist($newEntity);
}
$em->flush();

Symfony/Doctrine entity leads to dirty entity association when merged multiple times with entity manager

I am using Symfony2 and Doctrine
I have a doctrine entity which is serialized/unserialized to a session and used in multiple screens. This entity has a number of one to many associations.
The doctrine entity has the following one to many, for example:
class Article {
...
/**
* #ORM\OneToMany(targetEntity="image", mappedBy="article", cascade= {"merge","detach","persist"})
*/
protected $images;
public function __construct()
{
$this->images = new ArrayCollection();
}
.....
}
The article entity is saved and retrieved as follows:
public function saveArticle($article)
{
$articleSerialized = serialize($article);
$this->session->set('currentArticle',$articleSerialized);
}
public function getArticle()
{
$articleSerialized = $this->session->get('currentArticle');
$article = unserialize($articleSerialized);
$article = $this->em->merge($article);
return $article;
}
I am able to save and load the entity to and from the session any number of times, and then merge it back to the entity manager and save. This is only if it is a new entity.
However, once I load an entity from the db and then save it to session, I get problems.
I know, from other posts, that after you unserialise a saved entity, you have to run $em->merge($entity);
I am able to merge the entity, add a new sub-entity (one to many) and then save:
$article = $this->getArticle(); //Declared above, gets article from session
$image = new Image();
$image->setFilename('image.jpeg');
$article->addImage($image);
$this->saveArticle($article); //Declared above, returns the article to session
However, after the first merge and image add, I can't add any more sub-entities. If i try to add a second image, It returns the following error:
A managed+dirty entity <<namespace of entity>>
image#0000000067078d7400000000221d7e02 can not
be scheduled for insertion.
So in summary, i can make any number of changes to an entity and save it to session, but if I run $em->merge more than once while adding sub-entities, the new sub-entities are marked as dirty.
Does anybody know why an entity would be marked as dirty? Do I need to reset the entity itself, and if so, how could I do that?
Got it.
For anyone who might run into this problem in the future:
You cannot merge an entity which has unpersisted sub-entities. They become marked as dirty.
I.E
You may have an article with two images already saved to DB.
ARTICLE (ID 1) -> IMAGE (ID 1)
-> IMAGE (ID 2)
If you save serialise the article to session and then unserialize and merge it. It's ok.
If you add a new image, then serialize it to session you will have problems. This is because you cannot merge an unpersisted entity.
ARTICLE (ID 1) -> IMAGE (ID 1)
-> IMAGE (ID 2)
-> IMAGE (NOT YET PERSISTED)
What I had to do was:
After I unserialize the article, I remove the unpersisted images and store them in a temporary array (I check for ID). THEN i merge the article and re-add the unpersisted image(s).
$article = unserialize($this->session->get('currentArticle'));
$tempImageList = array();
foreach($article->getImages() as $image)
{
if(!$image->getId()) //If image is new, move it to a temporary array
{
$tempImageList[] = $image;
$article->removeImage($image);
}
}
$plug = $this->em->merge($article); //It is now safe to merge the entity
foreach($tempImageList as $image)
{
$article->addImage($image); //Add the image back into the newly merged plug
}
return $article;
I can then add more images if need be, and repeat the process until I finally persist the article back to DB.
This is handy to know in the event you need to do a multiple pages creation process or adding images via AJAX.

Magento: how to merge two product collections into one?

if i have two product collections is there a way to merge them into one?
for example (my final intent isn't to actually just get a collection of 2 cats, this is just to illustrate the issue):
$collection1 = Mage::getModel('catalog/category')->load(148)->getProductCollection();
$collection2 = Mage::getModel('catalog/category')->load(149)->getProductCollection();
$merged_collection = merge_collections($collection1,$collection2);
any help would be appreciated!
Assuming the items you wish to group together are of the same type and exist in the database then you can do this:
$collection1 = Mage::getModel('catalog/category')->load(148)->getProductCollection();
$collection2 = Mage::getModel('catalog/category')->load(149)->getProductCollection();
$merged_ids = array_merge($collection1->getAllIds(), $collection2->getAllIds());
// can sometimes use "getLoadedIds()" as well
$merged_collection = Mage::getResourceModel('catalog/product_collection')
->addFieldToFilter('entity_id', array('in' => $merged_ids))
->addAttributeToSelect('*');
Here I know to filter by entity_id because that is products' key field, like it is for most entity types, some flat tables have a different primary key. Often you can generalise that with one of the collection's getIdFieldName() method. Products are a bad example in this case because it's ID field name isn't filled out correctly.
Almost every (or every?) collection in Magento inherits from a Varien Data Collection. A collection is a special object that holds objects of another type. There's no method for merging collections, but you can add additional items of the appropriate type to the collection.
Code like this should get you where you want to go, although there's probably more efficient ways to loop and do the actual merging.
$collection1 = Mage::getModel('catalog/category')->load(148)->getProductCollection();
$collection2 = Mage::getModel('catalog/category')->load(149)->getProductCollection();
//load an empty collection (filter-less collections will auto-lazy-load everything)
$merged = Mage::getModel('catalog/product')->getCollection()->addFieldToFilter('entity_id',-1);
//add items from the first collection
foreach($collection1 as $item)
{
$merged->addItem($item);
}
//add items from the second collection
foreach($collection2 as $item)
{
//magento won't let you add two of the same thing to a collection
//so make sure the item doesn't already exist
if(!$merged->getItemById($item->getId()))
{
$merged->addItem($item);
}
}
//lets make sure we got something
foreach($merged as $product)
{
var_dump($product->getName());
}
I don't think there is such a method, but you can probably do something like that :
foreach ($collection1 as $item){
$collection2->addElem($item);
}
you can filter your collection directly without using 2.
$products = Mage::getModel('catalog/product');
$_collection = $products->getCollection();
->addCategoryFilter(2)
->load();
or try using 'addItem' to add your results to a collection. See also in Magento wiki
for the collection of products of several categories, you can use the following code
$collection = Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect('name')
->addAttributeToSelect('sku');
$collection->getSelect()
->join(
'catalog_category_product',
'product_id=entity_id',
array('category_id')
)
->where('catalog_category_product.category_id IN (?)', $categories);

Resources