I'm using doctrine and sf2 and i have a question about the slug extension :
Is there any way to generate it before the flush ?
Let say i have a Brand Entity:
/**
* Brand
*
* #ORM\Table(indexes={#ORM\Index(name="name_idx", columns={"name"})})
* #ORM\Entity(repositoryClass="Shoesalley\Bundle\CoreBundle\Entity\BrandRepository")
*/
class Brand
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #var string
*
* #Gedmo\Slug(fields={"name"})
* #ORM\Column(length=128, unique=true)
*/
private $slug;
}
// getters and setters ...
if i do this i get 2 differents slugs : test and test_1
$brand=new Brand();
$brand->setName('test');
$em->persist($brand);
$brand2=new Brand();
$brand2->setName('Test');
$em->persist($brand2);
The goal would be to find that the target slug allready exist and only have 1 DB Entry.
I can't use find() without a generated slug, so does anyone have an idea ?
The main idea is something like that, but i don't know how to implement it :
$brand=new Brand();
$brand->setName('test');
$slug = $brand->getSlug();
if( $oBrand = $em->getRepository("DemoBundle:Brand")->findOneBySlug($slug)){
$brand = $oBrand;
}
$em->persist($brand);
Thanks a lot for your help.
You are using the right logic in your solution i think; there are probalbly a few glitches you need to take care of though :
$brand = new Brand();
$brand->setName('test');
$slug = $brand->getSlug();
// Is getSlug() going to work before persist ?
// If not, you'll have to "simulate" the generation of a slug
// to obtain a string equivalent to that slug
$obrand = $em->getRepository("DemoBundle:Brand")->findOneBySlug($slug);
if(empty($obrand) { // empty, or is_null, depends on what your orm returns when it finds nothing
$em->persist(brand);
} else {
echo('Slug ' . $brand->getSlug() . ' is already in use !');
}
Related
Since upgrading to Symfony 2.7, I seem to keep getting 'circular reference has been detected' errors when attempting to serialize an array of contacts associated with a given group. They're setup in a many-to-many association (one group has many contacts; one contact has many group-associations).
Now, I followed the guide for using serialization groups as per the 2.7 upgrade post, but still seem to get the error. My controller code for this is currently as follows:
$group = $this->getDoctrine()
->getRepository('TwbGroupsBundle:ContactGroup')
->find($id);
$groupContacts = $group->getContacts();
$encoder = new JsonEncoder();
$normalizer = new ObjectNormalizer();
$serializer = new Serializer(array($normalizer), array($encoder));
$json = $serializer->serialize($groupContacts, 'json', array(
'groups' => array('public')
));
When running $serializer->serialize(), I get the CircularReferenceException after 1 circular reference. So far I have my Contact entity configured like so, with the #Groups annotations:
/**
* Contact
*
* #ORM\Table(name="tblContacts")
* #ORM\Entity(repositoryClass="Twb\Bundle\ContactsBundle\Entity\Repository\ContactRepository")
*/
class Contact implements ContactInterface
{
/**
* #var string
*
* #ORM\Column(name="ContactName", type="string", length=50, nullable=true)
* #Groups({"public"})
*/
private $contactname;
/**
* #var integer
*
* #ORM\Column(name="ContactID", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
* #Groups({"public"})
*/
private $contactid;
/**
*
* #var ArrayCollection
* #ORM\ManyToMany(targetEntity="Twb\Bundle\GroupsBundle\Entity\ContactGroup", inversedBy="contacts")
* #ORM\JoinTable(name="tblContactsGroupsAssignments",
* joinColumns={#ORM\JoinColumn(name="contactId", referencedColumnName="ContactID")},
* inverseJoinColumns={#ORM\JoinColumn(name="contactGroupId", referencedColumnName="id")}
* )
*/
protected $contactGroups;
// ...getters/setters and so on
}
And my ContactGroup entity:
/**
* ContactGroup
*
* #ORM\Table(name="tblContactsGroups")
* #ORM\Entity
*/
class ContactGroup
{
// ...
/**
*
* #var Contact
*
* #ORM\ManyToMany(targetEntity="Twb\Bundle\ContactsBundle\Entity\Contact", mappedBy="contactGroups")
*/
private $contacts;
// ...
}
Is there something I'm missing here to get around the circularity problem? Many thanks.
It looks like something wrong with config.
You have to enable serialization groups annotation:
# app/config/config.yml
framework:
# ...
serializer:
enable_annotations: true
And proper use statement has to be present in ContactGroup entity class
use Symfony\Component\Serializer\Annotation\Groups;
$normalizers->setCircularReferenceHandler(function ($object) {
return $object->getId();
});
Just add it after you make the instance of your objectNormalizer ;
Is there a possibility to read all available Values from an entity?
E.G.
class Properties
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="UserID", type="string", length=255)
*/
private $userID;
/**
* #var string
*
* #ORM\Column(name="Sport", type="string", length=1)
*/
private $sport;
.
.
.
So that I will get the name of the Value like: Id, UserID, Sport?
You can read the info you need thru the Doctrine metadata info as follow:
$doctrine = $this->getContainer()->get("doctrine");
$em = $doctrine->getManager();
$className = "Acme\DemoBundle\Entity\Properties";
$metadata = $em->getClassMetadata($className);
$nameMetadata = $metadata->fieldMappings['sport'];
echo $nameMetadata['type']; //print "string"
echo $nameMetadata['length']; // print "1"
// OR query for all fields
// Returns an array with all the identifier column names.
$metadata->getIdentifierColumnNames();
More info on the API DOC
Hope this help
You can make use of ReflectionClass::getProperties() to loop through all properties.
http://php.net/manual/en/reflectionclass.getproperties.php
Here's what I'm having trouble with.
I've a Table which contains a column called shown_on_homepage and only one row should be set to 1, the rest should all be set to 0. I'm trying to add a new row to the database and this one should be set to 1, setting the one that previously had a 1 to 0.
In MySQL I know this can be achieved by issuing an update before the insert:
UPDATE table_name SET shown_on_homepage = 0
Here's my Entity:
class FeaturedPerson {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="content", type="string", length=2500, nullable=false)
*/
private $content;
/**
* #var \DateTime
*
* #ORM\Column(name="date_updated", type="datetime")
*/
private $dateUpdated;
/**
* #var bool
*
* #ORM\Column(name="shown_on_homepage", type="boolean", nullable=false)
*/
private $isShownOnHomepage;
//...
public function getIsShownOnHomepage() {
return $this->isShownOnHomepage;
}
public function setIsShownOnHomepage($isShownOnHomepage) {
$this->isShownOnHomepage = $isShownOnHomepage;
return $this;
}
}
And for the Controller I've:
$featured = new FeaturedPerson();
$featured->setContent('Test content.');
$featured->setDateUpdated('01/02/2013.');
$featured->setIsShownOnHomepage(TRUE);
$em = $this->getDoctrine()->getManager();
$em->persist($featured);
$em->flush();
It does add the new row, but the one that had a shown_on_homepage set to 1 still has it. I've researched but I couldn't find a way to achieve this, I hope you can help me.
You could execute a query prior to your existing code in your controller:
$queryBuilder = $this->getDoctrine()->getRepository('YourBundleName:FeaturedPerson')->createQueryBuilder('qb');
$result = $queryBuilder->update('YourBundleName:FeaturedPerson', 'd')
->set('d.isShownOnHomepage', $queryBuilder->expr()->literal(0))
->where('d.isShownOnHomepage = :shown')
->setParameter('shown', 1)
->getQuery()
->execute();
Change 'YourBundleName' to your bundle name.
I have a Product class that has many fields on it for ManyToMany, such as ingredients, sizes, species, etc.. A total of about 14 different fields
Not all of the fields are are relevant to each product.
I have mapping set up like this
Class product {
/**
* #var Species[]
* #ORM\ManyToMany(targetEntity="Species")
* #ORM\JoinTable(name="product_species",
* joinColumns={#ORM\JoinColumn(name="productId", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="speciesId", referencedColumnName="id")}
* )
* #ORM\OrderBy({"name" = "asc"})
*/
private $species;
This works great for a manytomany/manyto one.
The problem is in my product_ingredients table I needed to add an additional field, meaning need to switch from ManyToMany to a OneToMany/ManyToOne
So like this
/**
* #var ProductIngredient[]
*
* #ORM\OneToMany(targetEntity="ProductIngredient", mappedBy="product")
* #ORM\JoinColumn(name="productId", referencedColumnName="id")
*/
private $ingredients;
Now my ProductIngredient Entity Looks like this
/**
* #var IngredientType
* #ORM\ManyToOne(targetEntity="IngredientType", fetch="EAGER")
* #ORM\JoinColumn(name="ingredientTypeId", referencedColumnName="id")
*/
private $ingredientType;
/**
* #var Ingredient
*
* #ORM\ManyToOne(targetEntity="Ingredient", inversedBy="products", fetch="EAGER")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="ingredientId", referencedColumnName="id")
* })
*/
private $ingredient;
/**
* #var Product
*
* #ORM\ManyToOne(targetEntity="Product", inversedBy="ingredients")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="productId", referencedColumnName="id")
* })
*/
private $product;
So in my product class for species I use the #ORM\OrderBy so that species is already ordered.. Is there a way I can somehow also do this for my ingredients field?
Or am I doing my logic wrong and these shouldn't even be fields on the product class and should just be looking up by the repository instead?
I was wanting it to be easy so I could loop through my objects like $product->getIngredients()
instead of doing
$ingredients = $this->getDoctrine()->getRepository('ProductIngredient')->findByProduct($product->getId());
in the Product entity just also aadd the orderBy to the ingredients relation
/**
* ...
* #ORM\OrderBy({"some_attribute" = "ASC", "another_attribute" = "DESC"})
*/
private $ingredients;
Well I came up with a hackish way.. Since I really only care about the sort on output, I have made a basic twig extension
use Doctrine\Common\Collections\Collection;
public function sort(Collection $objects, $name, $property = null)
{
$values = $objects->getValues();
usort($values, function ($a, $b) use ($name, $property) {
$name = 'get' . $name;
if ($property) {
$property = 'get' . $property;
return strcasecmp($a->$name()->$property(), $b->$name()->$property());
} else {
return strcasecmp($a->$name(), $b->$name());
}
});
return $values;
}
I would like to avoid this hack though and still would like to know a real solution
You should use 'query_builder' option in your Form: http://symfony.com/doc/master/reference/forms/types/entity.html#query-builder
The value of the option can be something like this:
function(EntityRepository $er) {
return $er->createQueryBuilder('i')->orderBy('i.name');
}
Don't forget to add the "use" statement for EntityRepository
If you use xml mapping, you could use
<order-by>
<order-by-field name="some_field" direction="ASC" />
</order-by>
inside your <one-to-many> tag.
I have a Product class that has many fields on it for ManyToMany, such as ingredients, sizes, species, etc.. A total of about 14 different fields
Not all of the fields are are relevant to each product.
I have mapping set up like this
Class product {
/**
* #var Species[]
* #ORM\ManyToMany(targetEntity="Species")
* #ORM\JoinTable(name="product_species",
* joinColumns={#ORM\JoinColumn(name="productId", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="speciesId", referencedColumnName="id")}
* )
* #ORM\OrderBy({"name" = "asc"})
*/
private $species;
This works great for a manytomany/manyto one.
The problem is in my product_ingredients table I needed to add an additional field, meaning need to switch from ManyToMany to a OneToMany/ManyToOne
So like this
/**
* #var ProductIngredient[]
*
* #ORM\OneToMany(targetEntity="ProductIngredient", mappedBy="product")
* #ORM\JoinColumn(name="productId", referencedColumnName="id")
*/
private $ingredients;
Now my ProductIngredient Entity Looks like this
/**
* #var IngredientType
* #ORM\ManyToOne(targetEntity="IngredientType", fetch="EAGER")
* #ORM\JoinColumn(name="ingredientTypeId", referencedColumnName="id")
*/
private $ingredientType;
/**
* #var Ingredient
*
* #ORM\ManyToOne(targetEntity="Ingredient", inversedBy="products", fetch="EAGER")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="ingredientId", referencedColumnName="id")
* })
*/
private $ingredient;
/**
* #var Product
*
* #ORM\ManyToOne(targetEntity="Product", inversedBy="ingredients")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="productId", referencedColumnName="id")
* })
*/
private $product;
So in my product class for species I use the #ORM\OrderBy so that species is already ordered.. Is there a way I can somehow also do this for my ingredients field?
Or am I doing my logic wrong and these shouldn't even be fields on the product class and should just be looking up by the repository instead?
I was wanting it to be easy so I could loop through my objects like $product->getIngredients()
instead of doing
$ingredients = $this->getDoctrine()->getRepository('ProductIngredient')->findByProduct($product->getId());
in the Product entity just also aadd the orderBy to the ingredients relation
/**
* ...
* #ORM\OrderBy({"some_attribute" = "ASC", "another_attribute" = "DESC"})
*/
private $ingredients;
Well I came up with a hackish way.. Since I really only care about the sort on output, I have made a basic twig extension
use Doctrine\Common\Collections\Collection;
public function sort(Collection $objects, $name, $property = null)
{
$values = $objects->getValues();
usort($values, function ($a, $b) use ($name, $property) {
$name = 'get' . $name;
if ($property) {
$property = 'get' . $property;
return strcasecmp($a->$name()->$property(), $b->$name()->$property());
} else {
return strcasecmp($a->$name(), $b->$name());
}
});
return $values;
}
I would like to avoid this hack though and still would like to know a real solution
You should use 'query_builder' option in your Form: http://symfony.com/doc/master/reference/forms/types/entity.html#query-builder
The value of the option can be something like this:
function(EntityRepository $er) {
return $er->createQueryBuilder('i')->orderBy('i.name');
}
Don't forget to add the "use" statement for EntityRepository
If you use xml mapping, you could use
<order-by>
<order-by-field name="some_field" direction="ASC" />
</order-by>
inside your <one-to-many> tag.