Maz
Maz

Hi. I'm working on a custom real estate plugin, and I wanted to create a simple button "preview" in the form create controller, to load the frontend page with the form data to preview the offer in a real situation.

I've succeed to pass my form data with a basic cookie which lives one minute and redirecting on request success to the front end view secured with a middleware to redirect all unlogged user to a 404.

But I'm having a headache about how to access the file: in my ajax handler, I've spent about 5 hours reading the docs, forum posts and trying to understand how to access the files: Input::file() > null, post('photos') > null, Input::get('photos') > null, (new File)->fromPost() > null, read and tried with deferred binding but can't achieve what I want... I've tried with photos and Land.photos (all my form field are "namespaced" with the model name) In my .yaml file I have this:

    photos:
        label: Photos
        mode: image
        imageWidth: '200'
        imageHeight: '200'
        useCaption: true
        type: fileupload

And my form button is there:

<?= Form::open(['class' => 'layout', 'files' => true]) ?>
        <div class="layout-row">
            <?= $this->formRender() ?>
        </div>
<button
    type="submit"
    data-request="onPreview"
    data-request-success="window.open(`/preview/land`, '_blank')"
    data-hotkey="ctrl+p, cmd+p"
    data-load-indicator="<?= e(trans('backend::lang.relation.preview')) ?>"
    class="btn btn-default">
    <?= e(trans('backend::lang.relation.preview')) ?>
</button>
<!-- I also tried with data-request-file attributes on form and button tag -->

And here is the ajax handler where I'm trying to access the files:

public function create_onPreview()
{
    // How to get the file here?

    $land = new Land(Input::get('Land'));

    return response(null, 200)->withCookie('previewCookie', $land, 1);
}

What I'm afraid of is the fact that maybe the ajax just can't pass the fileupload files on custom ajax handler...

Last updated

emmanuel.makgabo14416
emmanuel.makgabo14416

What do you get when dumping Input::all()

Maz
Maz

I'm having my full form value, the session key, but no mention of the photos field. I'm not on my laptop my laptop for now. Will edit this message to show you in 3 hours.

mjauvin
mjauvin

Try adding data-request-files to your form

Maz
Maz

Already tried, as said in the comment on my view file.

I also tried to reproduce the full form tag manually in plain html with the data-request-file.

Does the file field should appear in input::all() dump? I'm guessing it should appear at least the class name as I've already seen elsewhere in my plugins.

mjauvin
mjauvin

Did you check how plugins handling file uploads do it?

Maz
Maz

When I'm performing the create action, the file is correctly performed, I don't have "two form imbricated" actually. Here is my full create.htm, I've just change the "Save" button to a Preview one :

<?php Block::put('breadcrumb') ?>
    <ul>
        <li><a href="<?= Backend::url('romainmazb/realestate/lands') ?>">Lands</a></li>
        <li><?= e($this->pageTitle) ?></li>
    </ul>
<?php Block::endPut() ?>

<?php if (!$this->fatalError): ?>

    <?= Form::open(['class' => 'layout', 'files' => true]) ?>

        <div class="layout-row">
            <?= $this->formRender() ?>
        </div>

        <div class="form-buttons">
            <div class="loading-indicator-container">
                <button
                    type="submit"
                    data-request="onPreview"
                    data-request-success="window.open(`/preview/land`, '_blank')"
                    data-hotkey="ctrl+p, cmd+p"
                    data-load-indicator="<?= e(trans('backend::lang.relation.preview')) ?>"
                    class="btn btn-default">
                    <?= e(trans('backend::lang.relation.preview')) ?>
                </button>
                <button
                    type="button"
                    data-request="onSave"
                    data-request-data="close:1"
                    data-hotkey="ctrl+enter, cmd+enter"
                    data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
                    data-request-confirm="Confirmer la création et diffusion?"
                    class="btn btn-primary">
                    <?= e(trans('backend::lang.form.create_and_close')) ?>
                </button>
                <span class="btn-text">
                    <?= e(trans('backend::lang.form.or')) ?> <a href="<?= Backend::url('romainmazb/realestate/lands') ?>"><?= e(trans('backend::lang.form.cancel')) ?></a>
                </span>
            </div>
        </div>

    <?= Form::close() ?>

<?php else: ?>
    <p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
    <p><a href="<?= Backend::url('romainmazb/realestate/lands') ?>" class="btn btn-default"><?= e(trans('backend::lang.form.return_to_list')) ?></a></p>
<?php endif ?>

Here is the dump:

array:4 [
"_session_key" => "lq5cT3KzeNDu2epWGNJ8JBSgYRViXLnUptD3XHSw"
"_token" => "gSK8lGfiivwjz40GqTqi0fVZiIBUgeV7FHfVZJT0"
"Land" => array:21 [
"town" => "Taverny"
"price" => "150000"
"price_prefix" => ""
"price_suffix" => ""
"type" => "0"
"postal_code" => "95150"
"situation" => "1"
"surface" => "300"
"facade" => "16"
"utilities" => "1"
"cos" => "0.12"
"sanitation" => "2"
"level_diff" => "2"
"zone" => array:2 [
0 => "Calme"
1 => "Centre ville"
]
"conveniences" => array:2 [
0 => "Écoles"
1 => "Commerces"
]
"export" => "1"
"description" => "Some huge long text I've truncated"
"tile_front" => "Terrain de [surface] à [ville] ([département])\r\n"
"tile_back" => "[prix]€ pour ce superbe terrain à [ville]\r\n"
"selected_mixable_house_models" => "0"
"_activate_refresh_description" => "0"
]
"formExtraFields_loaded" => "1"
]
mjauvin
mjauvin

Try changing your:

<?= Form::open(['class' => 'layout', 'files' => true]) ?>

to:

<?= Form::ajax('onPreview', ['class' => 'layout', 'files' => true]) ?>

I've seen cases with file uploads where using Form::ajax instead of regular Form::open made a difference.

Maz
Maz

Same result, I've just also tried to generate the file field directly with the Form helper:

<?= Form::ajax('onPreview', ['class' => 'layout', 'files' => true]) ?>
<?= Form::file('image') ?>

And no mention of the image field in the Input::all() dump

I'm currently trying to reproduce the onSave native behavior in my onPreview ajax handler, at least the way this handler retrieve the formData but with no success:

"Undefined class constant 'CONTEXT_CREATE'" on line 165 of /home/maz/websites/me/plugins/romainmazb/realestate/controllers/Lands.php

And if I just pass the context to 'create', I'm facing the next line issue, and so on... Don't know why:

"Cannot access protected property Backend\Behaviors\FormController::$context" on line 382 of /home/maz/websites/me/vendor/october/rain/src/Extension/ExtendableTrait.php

Also a weird things, all the FormController "helper", such as formGetContext, formGetModel, formGetWidget and so on, return me 'null' ???

Can't understand why since I'm providing the FormController class in $implement:

class Lands extends Controller
{
    public $implement = [
        'Backend\Behaviors\ListController',
        'Backend\Behaviors\FormController',
    ];

Last updated

mjauvin
mjauvin

I assume you use a modern browser that supports FormData?

mjauvin
mjauvin

You might want to try a simple FRONT-END form with AJAX to see if you get the same problems... maybe this is a problem on the backend.

mjauvin
mjauvin

Maz, I tried with this very simple front-end form in a CMS page and it's working as expected:

url=/form
layout=default
==
function onPreview()
{
    debug(Input::all());
}
==
{{ form_ajax('onPreview', {'class':'layout', 'files':true}) }}
{{ form_file('image') }}
{{ form_submit('Submit') }}
{{ form_close }}

Note: I use the debug bar plugin

Last updated

mjauvin
mjauvin
debug
array:3 [▼
  "_session_key" => "AMo86tVwcLnuzXe7ju57HUIGpkrDZSuhEhrwoOau"
  "_token" => "bI336LMJ705GeLx8eVUuveX1qHlStzfbOgZ77d4O"
  "image" => UploadedFile {#1168 ▼
    -test: false
    -originalName: "Capture d’écran 2020-04-22 à 15.30.23.png"
    -mimeType: "image/png"
    -size: 131790
    -error: 0
    #hashName: null
    path: "/tmp"
    filename: "phpSn7aja"
    basename: "phpSn7aja"
    pathname: "/tmp/phpSn7aja"
    extension: ""
    realPath: "/tmp/phpSn7aja"
    aTime: 2020-05-02 13:09:58
    mTime: 2020-05-02 13:09:58
    cTime: 2020-05-02 13:09:58
    inode: 147460
    size: 131790
    perms: 0100600
    owner: 1194
    group: 1196
    type: "file"
    writable: true
    readable: true
    executable: false
    file: true
    dir: false
    link: false
  }
]
mjauvin
mjauvin

What does you AJAX handler looks like?

mjauvin
mjauvin

I ran some tests with a backend controller form with file attachments and it looks like they are not being sent to the AJAX handler.

So there seems to be a different behavior between frontend/backend

Maz
Maz

Yes, I'm using the last version of firefox.

I was making a frontend/backend test before writing the response. Yep, the front end works for me too... That's not a good news ;/

Any idea about how to access it? I've spent all of mine

Last updated

Maz
Maz

The fact that all the controller's getters doesn't work makes me feel like something else is happening here too.

I should be able to access the formGetWidget()->getSaveData() but I'm not.

[EDIT] I've tried in an other really basic FormController, I'm note able to access the getters too:

class Styles extends Controller
{
    public $implement = [
        'Backend\Behaviors\ListController',
        'Backend\Behaviors\FormController',
    ];

    public $listConfig = 'config_list.yaml';
    public $formConfig = 'config_form.yaml';

    public $requiredPermissions = [
        'manage_styles',
    ];

    public function __construct()
    {
        parent::__construct();
        BackendMenu::setContext('RomainMazB.RealEstate', 'settings-menu', 'styles-menu');
    }

    public function onPreview($context = null)
    {
        debug($this->formGetWidget()); // Return null
    }
}

Feeling like onSave native handler is magic :o

Last updated

mjauvin
mjauvin

Maz said:

The fact that all the controller's getters doesn't work makes me feel like something else is happening here too.

I should be able to access the formGetWidget()->getSaveData() but I'm not

This is a different issue. You'd need to run some form initialization code to get the form content in this way from the AJAX handler.

On another note, if the file upload field is defined DIRECTLY in the html markup (not in the model form fields definition file) and you add "data-request-files" to the BUTTON markup, that works...

We need to figure out why it doesn't get the file upload from the formRender()

mjauvin
mjauvin

To use the FormController methods and properties, you'd need to emulate this in your ajax handler:

    public function preview($recordId = null, $context = null)
    {   
        try {
            $this->context = strlen($context) ? $context : $this->getConfig('preview[context]', self::CONTEXT_PREVIEW);
            $this->controller->pageTitle = $this->controller->pageTitle ?: $this->getLang(
                "{$this->context}[title]",
                'backend::lang.form.preview_title'
            );

            $model = $this->controller->formFindModelObject($recordId);
            $this->initForm($model);
        }
        catch (Exception $ex) {
            $this->controller->handleError($ex);
        }
    }
mjauvin
mjauvin

This [old] forum post [may] address the file upload issue for a form controller:

https://octobercms.com/forum/post/batch-upload-and-create

Last updated

1-20 of 26