Keep the image after editing the form. - Cakephp 3

Friends,
I’m trying to edit a form with the upload field not required, that is, keeping the images that have been added.

I’m adding the images like this:

.
.
$product = $this->Products->newEntity();
        if ($this->request->is('post')) {
            if(!empty($this->request->data['img_product']['name'])){
                $filename = $this->request->data['img_product']['name'];
                $myext = substr(strchr($filename, "."), 1);
                $path = "img/img_products/".Security::hash($filename);
                $uploadFile = $path.$filename;
                if(move_uploaded_file($this->request->data['img_product']['tmp_name'], $uploadFile))
                {
                    $this->request->data['img_product'] = $filename;
                    $this->request->data['path'] = $path;

                }
                $product = $this->Product->patchEntity($produto, $this->request->getData());
                if ($this->Products->save($product)) {
                    $this->Flash->success(__('Produto salvo com sucesso!'));

                    return $this->setAction('index');
                }
                $this->Flash->success(__('error!'));
            }
            
            if(empty($this->request->data['img_product']['name'])){
                $this->request->data['img_product'] = "sem-imagem.jpg";
                $product->path = "img/img_products/";
                $product = $this->Produtos->patchEntity($produto, $this->request->getData());
                if ($this->Products->save($product)) {
                    $this->Flash->success(__('save'));

                    return $this->setAction('index');
                }
            }
  }
.
.

This works perfectly.
However, I want to edit this form without the need for the file field to be mandatory, that is, the image that was previously registered can be kept.

the products table has the img_product and path fields to save the image path.

The method of editing is as follows:

$product = $this->Products->findBySlug($slug)->firstOrFail();
        if ($this->request->is('post') || $this->request->is('put')) {
            if(!empty($this->request->data['img_product']['name'])){
                unlink(WWW_ROOT .$product->path.$product->img_product);
                $filename = $this->request->data['img_product']['name'];
                $myext = substr(strchr($filename, "."), 1);
                $path = "img/img_products/".Security::hash($filename).".".$myext;
                $uploadFile = $path.$filename;
                if(move_uploaded_file($this->request->data['img_product']['tmp_name'], $uploadFile))
                {
                    $this->request->data['img_product'] = $filename;
                    $this->request->data['path'] = $path;

                }
                $produto = $this->Products->patchEntity($produto, $this->request->getData());
                if ($this->Products->save($produto)) {
                    $this->Flash->success(__('save'));

                    return $this->setAction('index');
                }
            }
        $this->Flash->success(__('error'));

}

edit.ctp

<small>Image</small>
                  <?= $this->Form->control('img_product', [
                  'class' => 'form-control form-control-user',
                  'type' => 'file',
                  'allowempty' => true,
                  'label' => false,
                  ]);?>

ProductTable.php

         $validator
        ->scalar('img_product')
        ->maxLength('img_product', 255)
        ->allowEmptyString('img_product');

        $validator
        ->scalar('path')
        ->maxLength('path', 255)
        ->notEmptyString('path');

When the image field is not empty, the image is replaced normally. But I’m having trouble maintaining the image. If a new image is not inserted. I appreciate any comments.

The basic idea is to keep a previously registered image.

The nesting of the ifs in your edit method look strange.

The save only runs when there is a new file name.

How about this:

        $product = $this->Products->findBySlug($slug)->firstOrFail();
        if ($this->request->is('post') || $this->request->is('put')) {
            if (!empty($this->request->data['img_product']['name'])) {
                unlink(WWW_ROOT . $product->path . $product->img_product);
                $filename = $this->request->data['img_product']['name'];
                $myext = substr(strchr($filename, "."), 1);
                $path = "img/img_products/" . Security::hash($filename) . "." . $myext;
                $uploadFile = $path . $filename;
                if (move_uploaded_file($this->request->data['img_product']['tmp_name'], $uploadFile)) {
                    $this->request->data['img_product'] = $filename;
                    $this->request->data['path'] = $path;
                }
            }
            $produto = $this->Products->patchEntity($produto, $this->request->getData());
            if ($this->Products->save($produto)) {
                $this->Flash->success(__('save'));
                return $this->setAction('index');
            }
            $this->Flash->success(__('error'));
        }

I am getting this error.

'[hasErrors]' => true,
	'[errors]' => [
		'img_product' => [
			'scalar' => 'The provided value is invalid',
			'maxLength' => 'The provided value is invalid'
		]
	],
	'[invalid]' => [
		'img_product' => [
			'tmp_name' => '',
			'error' => (int) 4,
			'name' => '',
			'type' => '',
			'size' => (int) 0
		]
	],

My ProductTable.php

$validator
        ->scalar('img_product')
        ->maxLength('img_product', 255)
        ->allowEmptyString('img_product');

        $validator
        ->scalar('path')
        ->maxLength('path', 255)
        ->allowEmptyString('path');

tela

The error is for img_product and the validator you are showing is for img_producto.

Is this a typo in your code or your question?

That validation rule says image product can be an empty string, but the actual value appears to be an array and it is not empty.

Can you make the validator allow an empty string at img_product.name or change your code a little?

        $product = $this->Products->findBySlug($slug)->firstOrFail();
        if ($this->request->is('post') || $this->request->is('put')) {
            if (!empty($this->request->data['img_product']['name'])) {
               //handle the upload here
            } else {
                //unset request->data['img_product'] here
            }
            $produto = $this->Products->patchEntity($produto, $this->request->getData());
            if ($this->Products->save($produto)) {
                $this->Flash->success(__('save'));
                return $this->setAction('index');
            }
            $this->Flash->success(__('error'));
        }

I changed it to:

 $validator
 ->allowEmpty ('img_product'); 

in ProductTable.php

And now I get the empty string in:

.
if(move_uploaded_file($this->request->data['img_product']['tmp_name'], $uploadFile))
  {
  $this->request->data['img_product'] = $filename;
  $this->request->data['path'] = $path;
       
  }else {
  $produto->img_product = ' ';                
  }
.
.

This way I can leave the optional upload field, but as I get empty, the image that was supposed to be kept goes out, as I update the value of the img_product in the bank. Now the question arises of how to preserve the image. I appreciate any comments.

In Cake4x the request is now immutable. That might be the case in 3x also. If so you would have to move request->data() to a variable and use/modify that or use withData()

$patchCandidate = $this->request->getData();
$patchCandidate['img_product'] = '';

$this->Products->patchEntity($produto, $patchCandidate);)

//or
$this->request = $this->request->withData('img_product', '');

I believe it doesn’t work in cakephp 3.8. The path is preserved, but the img_product is empty and the image disappears.

if(move_uploaded_file($this->request->data['img_product']['tmp_name'], $uploadFile))
   {
    $this->request->data['img_product'] = $filename;
    $this->request->data['path'] = $path;
     }
   }else{
	$this->request = $this->request->withData('img_product', '');
  }

‘img_product’ => ‘’,
‘path’ => ‘img/img_products/7588510c5ab9b55517e26ed9c87b83d576010a2e’,

I’ll think about it and post the result.