atk4 / ui

Robust and easy to use PHP Framework for Web Apps
https://atk4-ui.readthedocs.io
MIT License
440 stars 105 forks source link

ContainsXxx field should be editable in UI #1860

Open abbadon1334 opened 1 year ago

abbadon1334 commented 1 year ago

Example code https://gist.github.com/abbadon1334/313e166efb598b8a416d6241c62ddbbb

if ContainsOne or ContainsMany fields in persistence are not null and an exception is raised : Critical Error Atk4\Data\ValidationException: ContainsXxx does not support unmanaged data modification

in Crud ContainsOne/ContainsMany must be not visible and should be managed by the framework when a model with those types of fields is added to crud

to solve some issues i already create these Helpers, but I like to have a way in the framework to avoid these helpers, any ideas?


    /**
     * Use on Form to manage ContainsOne fields
     * 
     * @param Layout|Form $form
     * @param string      $fieldName
     *
     * @throws Exception
     * @throws \Atk4\Data\Exception
     * @throws \Atk4\Ui\Exception
     */
    public static function addControlContainsOne($form, string $fieldName): void
    {
        $model = $form->model ?? $form->form->model;

        $ml = $form->addControl($fieldName . '_contains_one', [
            Multiline::class,
            'rowLimit'    => 1,
            'addOnTab'    => false,
            'caption'     => $model->getField($fieldName)->getCaption(),
            'renderLabel' => false,
        ], [
            'neverPersist' => true,
        ]);

        Multiline::assertInstanceOf($ml)->setModel($model->ref($fieldName)->getModel());
        $model->onHook(Model::HOOK_BEFORE_SAVE, function ($m, $update) use ($ml) {
            Multiline::assertInstanceOf($ml)->saveRows();
        });
    }

    /**
     * Use on Form to manage ContainsMany fields
     * 
     * @param Layout|Form $form
     * @param string      $fieldName
     *
     * @throws Exception
     * @throws \Atk4\Data\Exception
     * @throws \Atk4\Ui\Exception
     */
    public static function addControlContainsMany($form, string $fieldName): void
    {
        $model = $form->model ?? $form->form->model;

        $ml = $form->addControl($fieldName . '_contains_many', [
            Multiline::class,
            'addOnTab'    => false,
            'caption'     => $model->getField($fieldName)->getCaption(),
            'renderLabel' => false,
        ], [
            'neverPersist' => true,
        ]);

        Multiline::assertInstanceOf($ml)->setModel($model->ref($fieldName));
        $model->onHook(Model::HOOK_BEFORE_SAVE, function ($m, $update) use ($ml) {
            Multiline::assertInstanceOf($ml)->saveRows();
        });
    }

    /**
     * Call this on model before setModel to Table/Grid/Crud 
     */
    public static function removeContainsXxx(Model $model) {
        foreach($model->getReferences() as $refName => $ref) {
            if ($ref instanceOf ContainsBase) {
                $model->removeField($refName);
            }
        }
    }
mvorisek commented 1 year ago

ContainsXxx should imply nested CRUD, there is some old multiline demo for it https://github.com/atk4/ui/blob/51fb72710525baf51e98e479b0f4d8581845afa8/demos/form-control/multiline-containsmany.php

related with https://github.com/atk4/data/pull/1004 change made to address https://github.com/atk4/data/issues/881

containsXxx should behave like ref traversal, except the data are not in a different table, but instead of stored within the original table field

same for hintable

1:1 json/field data replace may be supported, but all insert/save/delete hooks must be save, ie. data must be always reparted and determined if added, changed or removed