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;
}
}