Symfony2 - DocumentChoiceList and MongoDB - mongodb

I have a problem when I'm trying to make a form with collections. I explain you what my current scenario is.
I've created two basic objects: Product and Category. And I've created two types for them as well: ProductType, CategoryType.
I have 3 categories and 1 product and I've associated the first couple of categories to the product. So, the product has two categories associated.
I want to create the Product's Form. In this form I want to show only the categories the product has, in a html select control, so the user can make future operations with these data.
I summarize you the key points.
My Product class has
class Product
{
...
#EmbedMany(targetDocument="Acme\StoreBundle\Document\Category")
protected $categories;
...
}
In my ProductType I have:
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('id')
->add('name')
->add('price')
->add('categories', 'document', array(
'class' => 'Acme\StoreBundle\Document\Category',
'choices' => $builder->getData()->getCategories()->toArray(),
'multiple' => 'true',
'property' => 'name'
))
;
}
I've tried with everything and the most accurate half solution was to create the categories property of the ProductType as a document and choose the options through the bind data come from the controller.
The point is, with this solution the ids, of the options controls, are the spl_object_hash set in the UnitOfWork class, not the ids of the original Category object.
My previous tryings:
I don't have problem when I use documents without previous choices, but I want only the categories that Product owns.
I don't have problem when I use a collection to show the list of categories (associating the CategoryType), but I don't know how to show this as a select control.
I can't use a query_builder over Category class because I can't query only the objects have the product id X, because Category object doesn't have any Product reference (and that's right).
Does anyone has a solution for this issue or other idea to solve this?
Thank you very much,
Ricky.

you say
I want to create the Product's Form. In this form I want to show only the categories the product has, in a html select control, so the user can make future operations with these data.
but then you go ahead and grab all the categories.
Why not keep things simple and grab the referenced categories off the product object?
$cats = $product->getCategories();
if(!is_null($cats) && $cats->count() > 0) {
$choices = $cats;
} else {
// grab all so you can have the use set them
$choices = $builder->getData()->getCategories()->toArray()
}

That's not the problem. The problem is when you work with Embedded Documents in MongoDB.
When you're working with these kind of documents, Doctrine asume (with right logic) that you're not going to have a "manual" reference of this embedded document in other collection.
Yes, that seems logic, but... why can't you have a simplified embed document who references other extended version in other collection?. In my opinion, this is a mistake.
As I explained in my question, the ODM makes the id with a hash of the object not with ID anotation. So, you can grab all the options, as you point wisely, but your ids will be different to your Embed's ids.
Thank you again,
Ricky.

Related

EF order by property of navigation property?

I have a query that looks like this:
db.Items.Where(c => c.Enabled)
.OrderBy(c => c.Vendor.Category.Select(b=> b.OrderPriority))
.ToList();
I'm getting an error saying I need to implement IComparable... I'm not sure that I'm doing the right thing - I just want to order my items the OrderPriority property of Category. Is there any way to order using a property of a navigation property?
Basically you are trying to order Items by a collection of OrderPrioritys. This is because a Vendor has multiple Categories (or may have, because it's a 1-n association).
I'm not sure if it is even possible what you're trying to express. If you want to order the Items by the highest OrderPriority found in one of its Vendor's categories, you could do
db.Items.Where(c=>c.Enabled)
.OrderBy(c => c.Vendor.Category.Max(b=> b.OrderPriority))

Design my Entity so that I can use an Entity Field for a specific use case

I've a Candidate entity which have an $xmlContent attribute, This attribute is used to get some references to other Entities (Country, Citizenship, ...), the $xmlContent value contain the Ids of these entities as follow,
<data>
<countryId>2</countryId>
<citizenship>4</citizenship>
<!-- ... -->
</data>
Note: I know, The model was badly designed! I can't modify it, This was one of the project weird constraints when I started working on it.
So, I created getters and setters for each Id of the $xmlContent value.
The problem,
I created a Form to edit a Candidate object (including the $xmlContent Ids), I then Added getters and setters to get these values (getCountry(), getCitizenship(), ...)
The problem is that I want to let the user choose the value of Country (for example) from a list of all available countries, I also have to put the right country as a default one.
I then decided to use an Entity Field,
->add('country', 'entity', array(
'class' => 'MyBundle:Country',
'query_builder' => function(CountryRepository $er) {
return $er->createQueryBuilder('c')
->orderBy('c.rank', 'ASC');
},
'property' => 'Name'
))
But, here I had another problem, the getter I'm using to get a candidate Country returns an "Id" but the Entity Field expect an Object.
My Question,
What is the best way to handle this?
My Constraint,
I want to keep my code as clean as possible :)
Since you said you added getters and setters, I guess you're allowed to modify the model, but not its representation in the database or the mapping, am I right?
I think your getters / setters should not return ids / change xmlContent. Instead, the getters should read the ids and build the correponding entites if they were not built previously. The setters should just modify the former objects, and you should implement lifecycle callbacks so that xmlContent is up to date before it is persisted.
That way, the form would easily be bound to your data.
Maybe you should look into DataTransformers and EventSubscribers.
You could use a DataTransformer to parse you xmlContent into discrete properties and populate their fields, e.g. country and citizenship.
Using an EventSubscriber you can a) on PRE_BIND reverseTransform the form data into xmlContent* and b) transform your form elements, e.g. fetch countries and create/alter a choice-element country from the data.
* DataTransformers won't work here, as you do not just want to reverseTransform, i.e. string into entity, but rather want to merge and transform data from multiple fields (country, citizenship, etc.) into a single field (xmlContent).

How make a form for many to many relations with additional columns in symfony2?

I have Classes like below :
Class Order
{
// Order have many Product
// One to Many -> ProductOrder
}
Class ProductOrder
{
$order
$product
$type
// other variable
}
Class Product
{
// Product has many Order
// One to Many -> ProductOrder
}
Basically I have #manytomany with additional columns, so I need to have ProductOrder in middle based on the note in yellow box here.
My question is how I can have one form with One Order and Multiple Products.
Something like How to setup a many to many form in Symfony2 but I need the middle UserTask class. If I use Many-to-many with no middle class I can create form easily and everything works fine, but having the middle class and the same functionality is what I need.
You need to use form collections.
My question & answer regarding form collections:
Form Collection Error
Form Collections from Symfony 2 docs:
http://symfony.com/doc/current/cookbook/form/form_collections.html
Symfony 2.0: Embedded Forms for Collections:
http://www.scott-sherwood.com/?p=90

Dynamic Form Generation with Symfony2

The scenario is pretty simple, I want to enable adding product attributes in my webhop. So, I have product table which holds product information (price, description...), attribute table which defines all possible product attributes (color, weight, power...), product_attributes table which connects this two and holds actual attribute value (red, 25kg, 51w...).
I would like to enable product form to enable adding/editing this attributes (attribute set is defined in attribute table).
I tried using collections, but that allowed me to only edit existing product attributes, not to add new.
I also experimented with event subscribers, but everything I add does not fit in my Product entity, it is just not that flexible to accept anything, it asks for attributes.
Is there any good way to solve this problem?
You can accomplish what you want with Embedding Collection of Forms, and using some jQuery magic to make it pretty.
It's explained in detail in the guide. With collections you need to use :
'allow_add' => true, 'by_reference' => false,
to allow the users to add new attributes.

How to model a n to m relation with an attribute in Entity framework without adding the extra table

I'm pretty new to the Entity framework and I'm modelling this simple structure:
With this model what I have is a Users class with a property UsersGroups (a collection of UserGroups objects).
I would like to have a Users class with a property like Groups with type Tuple or something like this (a new PriorizedGroup class, etc) that is much more related with the bussines.
Is this possible with the Entity framework?
Thanks in advance.
EDIT: If I were modeling the bussines objects I would create a User class with a Groups property that contained all the groups the user pertains with an extra property to store its priority (with a tuple, with an inherited class, as you wish). The thing is that I feel that the objects created by the Entity framework resemble the SQL structure, not the business structure.
Not directly. EF can map the relation only in the way you see it at the moment but you can add your custom behavior to your partial part of the entity. The simple way is something like
public partial class Users
{
public IEnumerable<PrioritizedGroup> Groups
{
get
{
return UserGroups.Select(ug => new PrioritizedGroup
{
Priority = ug.Priority,
Id = ug.Group.Id,
Name = ug.Group.Name,
Description = ug.Group.Description
})
.OrderBy(g => g.Priority);
}
}
}
To make this happen directly in EF you need some advanced mapping technique which will require you to modify EDMX source code directly (either DefiningQuery or QueryView) and it will make the entity read only (you will need stored procedures for modification).
To make the collection exposed on Users updatable you would probably need to use ObservableCollection and transfer all modifications triggered by ObservableCollection back to original UserGroups collection. Once you have something like that implemented you can hide original collection.