Adding new entity through foreign controller with custom form input names

I set up XController->add() to create a new entity for Products.
public function add()
{
$this->loadModel(‘Products’);

$newProduct= $this->Products->newEmptyEntity();
$product_form_input=[‘product_name’,‘product_size’,‘product_brand’,‘product_category_id’];
$product_fields = array_intersect_key($this->request->getData(), array_flip($product_form_input));

$newProduct = $this->Products->patchEntity($newProduct, $product_fields);

if ($this->Products->save($newProduct)) {
$this->Flash->success(__(‘The new Product has been saved.’));
…}

However, I can’t find in the documentation how to match the required input of patchEntity to make the save successful. In ProductsTable.php I used the product_ prefix in the validator, although this was my best clue rather than understanding:

e.g. $validator
        ->scalar('product_name')
        ->maxLength('product_name', 64)
        ->requirePresence('product_name', 'create')
        ->notEmptyString('product_name'); 

Debugging indicates that this does lead to correct validations, although no new product is added to the DB (PhpMyAdmin).
I assume I have to explicitly match the table names without the product-prefix, to the ones with the prefix coming from the form. I don’t want to use the field names without the prefix, since I would like to use the same form for name fields of several models (such that getData contains both X_name as Y_name etc.)
For confirmation: the product_ fields are form inputs from the add() template of X.

Moreover, I want to let the DB create the id (which is done by ->allowEmptyString('id', null, 'create')) , but access this immediately after saving such that it can be given to a new entity of X. Is this possible?

It’s not entirely clear here what you’re trying to do. You want to have fields in the form with different names than they do in the database? So your form has controls named product_name, product_size, etc., but your Products table has just name, size, etc.? What you provide to patchEntity (and the names of the fields you validate on) must exactly match the table, so if there’s some mapping to be done then you need to do that yourself. Looks like you’re maybe trying to do that with the array_intersect_key call? Does the resulting $product_fields have the keys you expect to see?

Anyway, if you proceed with this, the next issue you’re going to have is that any validation errors won’t show up in your form, because the field names in the entity won’t match the form any more.

This overall seems like a case of overly aggressive optimization. Lots of times that I’ve seen symmetries between things which don’t really pan out and just end up making everything more complicated. Code should be DRY, but not too dry; just like many things in the real world, if it gets too dry, it gets brittle and is likely to break. :slight_smile:

If you really need to do this, I’d suggest that you move the “prefix” handling to the template instead of the controller, e.g. $this->Html->control("{$prefix}name"), where $prefix may be product_ or it might be blank, depending on who is calling it.

Correct. For the exact reason you are confirming.

That’s clear then, thanks.

No, with this I extract only the relevant fields of getData(). Since getData(‘x’, ‘y’, ‘z’) retrieves only the first argument. But this may be superfluous if the patchEntity automatically addresses the right fields, that I don’t know and is partly the reason for issuing this topic. I assumed the complete getData() as input would be troublesome as it contains form data of multiple models.

Well, I am unsure. I am quite sure it is not, respecting your experience and indication of doubt.
This is just another attempt to enable one user-facing form to possibly create entities of different models. This is one way of trying next to Nested add forms of multiple tables via form buttons. I figured multiple models need to be handled in one controller function/method, one way or another.

patchEntity is going to take whatever values are in the provided data and set them under the exact same keys in the entity (assuming that the accessible setting allows it). So, if you give it an array with a product_name key in it, that value is going to be in the product_name property of the entity. If there’s no field called that in the database, then it’s not going to be saved anywhere. You need to make sure that what you’re giving it has a key just called name if that’s what the database is expecting.

You can do that with fancy coding in the controller (accounting for both the incoming data format and the required difference in validation errors), or you could do that same stuff in places like beforeMarshal, or you can just save yourself the headaches and have a different form for this particular model. There’s a famous saying: “Premature optimization is the root of all evil.” How you’re trying to do it feels like premature optimization to me.