We are moving our forum in GitHub Discussions. For questions about Phalcon v3/v4 you can visit here and for Phalcon v5 here.

Solved thread

This post is marked as solved. If you think the information contained on this thread must be part of the official documentation, please contribute submitting a pull request to its repository.

Upload Image. Model Behavior

Upload Image via Model Behavior.

This is not a issue. I'm just sharing my solution. Maybe it will help beginners

<?php

namespace YourVendorHere\Models\Behavior;

use Phalcon\Mvc\Model\Behavior;
use Phalcon\Mvc\Model\BehaviorInterface;
use Phalcon\Mvc\ModelInterface;
use Phalcon\Mvc\Model\Exception;
use Phalcon\Logger;
use Symfony\Component\Filesystem\Filesystem; // Not necessarily, just convenient

class Imageable extends Behavior implements BehaviorInterface
{
    /**
     * Upload image path
     * @var string
     */
    protected $uploadPath = null;

    /**
     * Model field
     * @var null
     */
    protected $imageField = null;

    /**
     * Old model image
     * @var string
     */
    protected $oldFile = null;

    /**
     * Application logger
     * @var \Phalcon\Logger\Adapter\File
     */
    protected $logger = null;

    /**
     * Filesystem Utils
     * @var \Symfony\Component\Filesystem\Filesystem
     */
    protected $filesystem = null;

    /**
     * Allowed types
     * @var array
     */
    protected $allowedFormats = ['image/jpeg', 'image/png', 'image/gif'];

    public function notify($eventType, ModelInterface $model)
    {
        if (!is_string($eventType)) {
            throw new Exception('Invalid parameter type.');
        }

        // Check if the developer decided to take action here
        if (!$this->mustTakeAction($eventType)) {
            return;
        }

        $options = $this->getOptions($eventType);

        if (is_array($options)) {
            $this->logger = $model->getDI()->get('logger');
            $this->filesystem = new Filesystem;

            $this->setImageField($options, $model)
                 ->setAllowedFormats($options)
                 ->setUploadPath($options)
                 ->processUpload($model);
        }
    }

    protected function setImageField(array $options,  ModelInterface $model)
    {
        if (!isset($options['field']) || !is_string($options['field'])) {
            throw new Exception("The option 'field' is required and it must be string.");
        }

        $this->imageField = $options['field'];
        $this->oldFile = $model->{$this->imageField};

        return $this;
    }

    protected function setAllowedFormats(array $options)
    {
        if (isset($options['allowedFormats']) && is_array($options['allowedFormats'])) {
            $this->allowedFormats = $options['allowedFormats'];
        }

        return $this;
    }

    // Symfony\Component\Filesystem\Filesystem uses here, you can do it otherwise
    protected function setUploadPath(array $options)
    {
        if (!isset($options['uploadPath']) || !is_string($options['uploadPath'])) {
            throw new Exception("The option 'uploadPath' is required and it must be string.");
        }

        $path = $options['uploadPath'];

        if (!$this->filesystem->exists($path)) {
            $this->filesystem->mkdir($path);
        }

        $this->uploadPath = $path;

        return $this;
    }

    protected function processUpload(ModelInterface $model)
    {
        /** @var \Phalcon\Http\Request $request */
        $request = $model->getDI()->getRequest();

        if (true == $request->hasFiles(true)) {
            foreach ($request->getUploadedFiles() as $file) {
                // NOTE!!!
                // Nothing was validated here! Any validations must be are made in a appropriate validator
                if ($file->getKey() != $this->imageField || !in_array($file->getType(), $this->allowedFormats)) {
                    continue;
                }

                $uniqueFileName = time() . '-' . uniqid() . '.' . strtolower($file->getExtension());

                if ($file->moveTo(rtrim($this->uploadPath, '/\\') . DIRECTORY_SEPARATOR . $uniqueFileName)) {
                    $model->writeAttribute($this->imageField, $uniqueFileName);
                    $this->logger->log(Logger::INFO, sprintf(
                        'Success upload file %s into %s', $uniqueFileName, $this->uploadPath
                    ));

                    // Delete old file
                    $this->processDelete();
                }
            }
        }

        return $this;
    }

    // Symfony\Component\Filesystem\Filesystem uses here, you can do it otherwise
    protected function processDelete()
    {
        if ($this->oldFile) {
            $fullPath = rtrim($this->uploadPath, '/\\') . DIRECTORY_SEPARATOR . $this->oldFile;

            try {
                $this->filesystem->remove($fullPath);
                $this->logger->log(Logger::INFO, sprintf('File %s deleted successful.', $fullPath));
            } catch(\Exception $e) {
                $this->logger->log(Logger::ALERT, sprintf(
                    'An error occurred deleting file %s: %s', $fullPath, $e->getMessage()
                ));
            }
        }
    }
}

And just use in model:

namespace YourVendorHere\Models;

use Phalcon\Mvc\Model;
use YourVendorHere\Models\Behavior\Imageable;

class Product extends Model
{
    protected $image;

    public function initialize()
    {
        /** @var \Phalcon\Config $config */
        $config = $this->getDI()->get('config');

        if (!isset($config->media) || !isset($config->media->upload_path) || !is_string($config->media->upload_path)) {
            throw new Exception("The config param '\$config->media->upload_path' is required and it must be string.");
        }

        $this->uploadPath = rtrim($config->media->upload_path, '/\\') . DIRECTORY_SEPARATOR .'product';

        // NOTE: You need validate image (file size, file format, etc.)

        $this->addBehavior(new Imageable([
            'beforeCreate' => [
                'field'      => 'image',
                'uploadPath' => $this->uploadPath,
            ],
            'beforeUpdate' => [
                'field'      => 'image',
                'uploadPath' => $this->uploadPath,
            ],
        ]));
    }

    public function getImage()
    {
        return $this->image;
    }

    public function setImage($image)
    {
        $this->image = $image;

        return $this;
    }
}


58.2k
Accepted
answer

Hey @sergeyklay

This is tip, if so share here http://phalcontip.com