Forms - Managing OneToMany collection elements - doctrine-orm

Let's imagine these 2 entities:
ShoppingCart
creationDate
Item
name
shoppingCart # ManyToOne
I'm managing a ShoppingCart in a form with a CollectionType of Items
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
$builder
// ...
->add('items', CollectionType::class, array(
'entry_type' => ItemFormType::class,
// ...
}
I'd like my users to be able to add/remove items, which seems to me like a quite common need.
If I understand well, here is what I have to do:
Define the reverse OneToMany side of the ShoppingCart relation to Item
Make sure this relation have the cascade={"persist"} and orphanRemoval=true options
Initialize this items Collection into the constructor
Set the allow_add and allow_delete form option to true
Set the by_reference form option to false
Make sure andItem() & removeItem() functions are implemented into ShoppingCart
Set/unset the owning side relation to the ShoppingCart in addition to add/remove the Item from the ShoppingCart items collection
Keep the collection indexes within javascript so Doctrine can keep track of references
Am I forgetting something ?
I find it a big pain in the arse for me compared to the need and to other frameworks/ORMs
Is there a way to do it simpler ?

As I understand, your question is "why is it so complicated", isn't it?
Well, it's not. You've listed things multiple layers in your project. To exaggerate the point a little bit - why didn't you wrote that in order to create this form you need to install Symfony or even turn on your computer. ;-)
Anyway, most of these things are not required, others should be done before anyway.
Let's start with your OneToMany relation (Doctrine level).
ShoppingCart is your aggregate root (DDD term, but it doesn't matter if you follow DDD idea), which means you probably will do most of work on this object which will handle Items inside. If so, then you should have done inversed side of the relation, which covers these points (I think you went into details so much to make your list longer ;-), they all should be in one list item), but it takes a minute to do this stuff:
Define the reverse OneToMany side of the ShoppingCart relation to Item
Make sure this relation have the cascade={"persist"} and orphanRemoval=true options
Initialize this items Collection into the constructor
Make sure andItem() & removeItem() functions are implemented into ShoppingCart
Set/unset the owning side relation to the ShoppingCart in addition to add/remove the Item from the ShoppingCart items collection
This has nothing to do with forms. It's about how you want your entities to work with Doctrine.
Following two are not required:
Set the allow_add and allow_delete form option to true
Set the by_reference form option to false
They are about enabling/disabling features. You want them, so you need to use them. That's obvious and still not so much to do.
Last one:
Keep the collection indexes within javascript so Doctrine can keep track of references
This is the one that I actually don't understand. But what you need (in your case, because you're using allow_add/delete feature), is to provide JavaScript code that will create HTML for new row or delete existing one. But that's also just about copying prototype code. Also there are ready examples in Symfony's doc.

Let me answer myself in a simpler manner:
No I am not forgetting anything
No we can't do simpler
Some reasons are explained in #dragoste answer

Related

Ordered ManyToMany relation in Django with custom Field

In Django, I would like to have an ordered many-to-many-relation. Assume I have, say, the models OrderedList and Item, and I want to be able to insert Item()s into an OrderedList() at a specific index, I want to be able to retrieve the Item()s of an OrderedList() in their order and also to change the order of Item()s on an OrderedList
I already found Define an order for ManyToManyField with django and https://github.com/gregmuellegger/django-sortedm2m
Both the github repo and the accepted answer in the SO question are working with the same architecture: They create an additional integer field, say order_index, on the junction ("Through") table which represents the position of the Item() on the OrderedList().
Honestly, I do not like that too much. If I see this correctly, having the order stored on the junction table can create inefficiency when I want to reorder Item()s: Imagine, I want to change the position of an Item() on an OrderedList() which has n Item()s. This means O(n) database updates to reorganize the order indices.
I would like to avoid this. I think of an architecture where I have an ordinary many-to-many-relation and one additional column on the OrderedList table which holds a list of Item ids, say items_order. In this architecture, I need one database update and one list operation on items_order - which should be way faster, I guess.
I believe the best way for this is to create a custom model Field. The docs state how to create a custom model Field (https://docs.djangoproject.com/en/2.1/howto/custom-model-fields/) and I can create my items_order field like this. But I did not find how to make a custom Field which, besides creating the order_list, also creates the junction table and takes care of updating the items_order whenever a new related Item() is added or removed from the relation. I think, I should subclass the ManyToMany Field (https://docs.djangoproject.com/en/2.1/_modules/django/db/models/fields/related/#ManyToManyField). But I don't know how to do this, so could you give me some guidance here?

Basic List on Entity Form

In Dynamics CRM online, I can add a list of entities to another entity, for example a list of products to an opportunity.
Is there any way I can have a list that is not picked from pre-populated items, e.g. just a simple list of {number, date, text} that you type in each time you want to add to the list, not picking items from a pre-defined list.
I am just using the web interface to customise at the moment, but I am open to any suggestions.
EDIT:
So far i have;
Created two entities, proposal and proposal version
Added a 1:N relationship between proposal and proposal version
Added a sub-grid to the proposal form, tried to make it editable but it refuses to work
This lets me add new rows by opening up the proposal version form and adding a new one or picking from already created ones for other proposals but that is rather clunky for a simple list.
I don't want it to offer to search for previous entries, just let me add to the list by typing stuff in, surely this should be fairly simple?
If you want a pre-defined list of items that are simple (number, date, text..) then you can create an option set field in CRM. These lists are fixed and can only be extended by customising the system. An example option set field might be Organisation Type:
Prospect
Site
Head Office
...
If you want a pre-defined list that can be extended, you need to create a new entity. Following from the previous example, you would create a custom entity called Organisation Type and then create a record for each type you wanted, populating only the name field with the type: Prospect, Site etc.
Then you would add a lookup field pointing to the Organisation Type entity on any other entity that used the field, such as Organisation (Account).
You see how the custom entity still appears as simple data because you're only populating the name field, which can be text, a number etc. You can also apply security roles to this entity, limiting which users can create and delete options from your list.
Edit: to only allow the creation of new records in a subgrid, make sure the lookup attribute to the parent entity on the child entity is business required.

How to force Acceleo to iterate over a collection in the same order on every template execution?

I'm using an EMF model based on Modisco KDM metamodel. At some point of my Acceleo template I need to iterate over a collection, e.g.:
[for (e: AbstractCodeElement | action.codeElement) separator(', ')][e.generateCode() /]
The action.codeElement is a collection and modisco's kdm.ecore metamodel defines it as non-ordered.
Every time I run my generator, the output is generated on a different order. Cleary the serialized model xmi enforces a specific order, and every model editor (emf default editor, modisco editor) I open the model shows the same order always (matching the order the elements were serialized to the xmi file).
Since I cannot change the kdm.ecore metamodel to make the set ordered, would there be a workaround to get Acceleo to always iterate on the same order?
Thanks in advance
I'm afraid you can't.
Try and cast it to a sequence:
action.codeElement->asSequence()
but I don't think anything guarantees that the sequence you get will always be sorted in the same order.
If the metamodel is made that way, there should be a reason, so either you can contact the metamodel authors to check this reason, or you should sort the result of action.codeElement with some stable criterion:
action.codeElement->sortedBy( some OCL expression)
I don't know of a clean way. I solved the problem by altering the name attribute of a child element so that it was sortable alphabetically in the way I wanted.
I wanted Slots in the same order every time, so I changed the name of each of their "value" child.
The names looked like: "01_id", "02_username", "03_city", ... "10_instructions", "11_contact". I didn't have to change what the "value" elements held, just their name, which I wasn't using for anything anyways. Hope this helps.
[for (s : Slot | instanceSpecification.slot->select(definingFeature.name = 'column')->sortedBy(value->asSequence()->first().name)]
... do work here ...
[/for]

Supporting multi-add/delete (and undo/redo) with a QAbstractItemModel (C++)

Greetings,
I've been writing some nasty code to support the undo/redo of deletion of an arbitrary set of objects from my model. I feel like I'm going about this correctly, as all the other mutators (adding/copy-pasting) are subsets of this functionality.
The code is nastier than it needs to me, mostly because the only way to mutate the model involves calling beginInsertRows/beginRemoveRows and removing the rows in a range (just doing 1 row at a time, no need to optimize "neighbors" into a single call yet)
The problem with beginInsertRows/beginRemoveRows is that removal of a row could affect another QModelIndex (say, one cached in a list). For instance:
ParentObj
->ChildObj1
->ChildObj2
->ChildObj3
Say I select ChildObj1 and ChildObj3 and delete them, if I remove ChildObj1 first I've changed ChildObj3's QModelIndex (row is now different). Similar issues occur if I delete a parent object (but I've fixed this by "pruning" children from the list of objects).
Here are the ways I've thought of working around this interface limitation, but I thought I'd ask for a better one before forging ahead:
Move "backwards", assuming a provided list of QModelIndices is orderered from top to bottom just go from bottom up. This really requires sorting to be reliable, and the sort would probably be something naive and slow (maybe there's a smart way of sorting a collection of QModelIndexes? Or does QItemSelectionModel provide good (ordered) lists?)
Update other QModelIndeces each time an object is removed/added (can't think of a non-naive solution, search the list, get new QModelIndeces where needed)
Since updating the actual data is easy, just update the data and rebuild the model. This seems grotesque, and I can imagine it getting quite slow with large sets of data.
Those are the ideas I've got currently. I'm working on option 1 right now.
Regards,
Dan O
Think of beginRemoveRows/endRemoveRows, etc. as methods to ask the QAbstractItemModel base class to fix up your persistent model indexes for you instead of just a way of updating views, and try not to confuse the QAbstractItemModel base class in its work on those indexes. Check out http://labs.trolltech.com/page/Projects/Itemview/Modeltest to exercise your model and see if you are keeping the QAbstractItemModel base class happy.
Where QPersistentModelIndex does not help is if you want to keep your undo/redo data outside of the model. I built a model that is heavily edited, and I did not want to try keeping everything in the model. I store the undo/redo data on the undo stack. The problem is that if you edit a column, storing the persistent index of that column on the undo stack, and then delete the row holding that column, the column's persistent index becomes invalid.
What I do is keep both a persistent model index, and a "historical" regular QModelIndex. When it's time to undo/redo, I check if the persistent index has become invalid. If it has, I pass the historical QModelIndex to a special method of my model to ask it to recreate the index, based on the row, column, and internalPointer. Since all of my edits are on the undo stack, by the time I've backed up to that column edit on the undo stack, the row is sure to be there in the model. I keep enough state in the internalPointer to recreate the original index.
I would consider using a "all-data" model and a filter proxy model with the data model. Data would only be added to the all-data model, and never removed. That way, you could store your undo/redo information with references to that model. (May I suggest QPersistentModelIndex?). Your data model could also track what should be shown, somehow. The filter model would then only return information for the items that should be shown at a given time.

Subsonic 3 and table relationships

I have something like this:
Order order = new Order();
Item item = new Item();
order.Items.Add(item);
order.Save();
How can I do this with Subsonic? The method that refere to a related table is IQueryable.
You have three options:
Set the foreign key in Item to the id of your Order object and save both.
Create a partial class which has a method "AddItem", encapsulating this functionality
Modify the T4 templates to allow you to do this automatically; unfortunately this feature doesn't come out of the box yet.
The advantage with Subsonic is that it is flexible, however you occasionally have to fill some of the gaps yourself.
If you are programming something like a shopping cart you can abstract that out into it's own class that can handle marrying the objects together. I personally think it works better than modify the generated objects.