2
\$\begingroup\$

Recently I have been looking into Symfony framework and I am working on a task where I need to upload a file using a form. Once the form has been submitted I need to insert the user input along with image name and path in database using Symfony framework strictly (not standard PHP), the upload process also need to consider if the image already exist, and if yes, then rename it so it does not get over-written.

Although, the following code works, but I wanted to ask here if this is the right way to do it.

(DefaultController.php)

This is my controller in this the function createBlogAction() handles the form submission, if the form is valid this functions calls another function which resides in Entity/Post.php to upload the file

namespace ClickTeck\BlogBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use ClickTeck\BlogBundle\Entity\Post;
use ClickTeck\BlogBundle\Form\PostCreate;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\File\UploadedFile;

class DefaultController extends Controller
{
    public function createBlogAction(Request $request)
    {
        $post = new Post();
        $form = $this->createForm(new PostCreate(), $post);
        $form->handleRequest($request);
        if ($form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $post->upload();
            $post->setDate(date_create(date('Y-m-d H:i:s')));
            $post->setAuthor('ClickTeck');
            $em->persist($post);
            $em->flush();
            $this->get('session')->getFlashBag()->add(
                'notice',
                'Thank you!'
            );
        }
        return $this->render('BlogBundle:Default:blog-create.html.twig', array(
                'form' => $form->createView()
            )
        );
}

(Post.php)

In this file the function upload() uploads the file, it is this function I need confirmation on if this is the right way/best practice to upload image using Symfony 2.5

namespace ClickTeck\BlogBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;

/**
 * Post
 */
class Post
{
    /**
     * @var integer
     */
    private $id;

    /**
     * @var string
     */
    private $title;

    /**
     * @var string
     */
    private $description;

    /**
     * @var \DateTime
     */
    private $date;

    /**
     * @var string
     */
    private $category;

    /**
     * @var \DateTime
     */
    private $updated;

    /**
     * @var string
     */
    private $author;

    /**
     * @var string
     */
    private $image;

    /**
     * @var string
     */
    private $comments;

    /**
     * @var string
     */
    private $path;


    /**
     * Get id
     *
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set title
     *
     * @param string $title
     * @return Post
     */
    public function setTitle($title)
    {
        $this->title = $title;

        return $this;
    }

    /**
     * Get title
     *
     * @return string
     */
    public function getTitle()
    {
        return $this->title;
    }

    /**
     * Set description
     *
     * @param string $description
     * @return Post
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Get description
     *
     * @return string
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Set date
     *
     * @param \DateTime $date
     * @return Post
     */
    public function setDate($date)
    {
        $this->date = $date;

        return $this;
    }

    /**
     * Get date
     *
     * @return \DateTime
     */
    public function getDate()
    {
        return $this->date;
    }

    /**
     * Set category
     *
     * @param string $category
     * @return Post
     */
    public function setCategory($category)
    {
        $this->category = $category;

        return $this;
    }

    /**
     * Get category
     *
     * @return string
     */
    public function getCategory()
    {
        return $this->category;
    }

    /**
     * Set updated
     *
     * @param \DateTime $updated
     * @return Post
     */
    public function setUpdated($updated)
    {
        $this->updated = $updated;

        return $this;
    }

    /**
     * Get updated
     *
     * @return \DateTime
     */
    public function getUpdated()
    {
        return $this->updated;
    }

    /**
     * Set author
     *
     * @param string $author
     * @return Post
     */
    public function setAuthor($author)
    {
        $this->author = $author;

        return $this;
    }

    /**
     * Get author
     *
     * @return string
     */
    public function getAuthor()
    {
        return $this->author;
    }

    /**
     * Set image
     *
     * @param string $image
     * @return Post
     */
    public function setImage(UploadedFile $image = null)
    {

        $this->image = $image;

        return $this;
    }

    /**
     * Get image
     *
     * @return string
     */
    public function getImage()
    {
        return $this->image;
    }

    /**
     * Set comments
     *
     * @param string $comments
     * @return Post
     */
    public function setComments($comments)
    {
        $this->comments = $comments;

        return $this;
    }

    /**
     * Get comments
     *
     * @return string
     */
    public function getComments()
    {
        return $this->comments;
    }

    public function getAbsolutePath()
    {
        return null === $this->path
            ? null
            : $this->getUploadRootDir() . '/' . $this->path;
    }

    public function getWebPath()
    {
        return null === $this->path
            ? null
            : $this->getUploadDir() . '/' . $this->path;
    }

    protected function getUploadRootDir()
    {
        // the absolute directory path where uploaded
        // documents should be saved
        return __DIR__ . '/../../../../web/' . $this->getUploadDir();
    }

    protected function getUploadDir()
    {
        // get rid of the __DIR__ so it doesn't screw up
        // when displaying uploaded doc/image in the view.
        return 'uploads/blogImages';
    }

    public function upload()
    {
        // the file property can be empty if the field is not required
        if (null === $this->getImage()) {
            return;
        }
        $dirpath = $this->getUploadRootDir();
        $image = $this->getImage()->getClientOriginalName();
        $ext = $this->getImage()->guessExtension();
        $name = substr($image, 0, - strlen($ext));
        $i = 1;
        while(file_exists($dirpath . '/' .  $image)) {
            $image = $name . '-' . $i .'.'. $ext;
            $i++;
        }
        $this->getImage()->move($dirpath,$image);
        $this->image = $image;
        $this->path = $this->getUploadDir();
        $this->file = null;
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ I'm currently looking into exactly this problem. Your code is basically what the offical article suggests, however I don't consider it best practice. The biggest problem IMHO are the hard coded web and upload directories in the entity. I found a stackoverflow question that (partially) covers this, but it doesn't satisfy my really. \$\endgroup\$
    – RoToRa
    Nov 26, 2014 at 9:07
  • \$\begingroup\$ Thanks for the feedback. I guess with Symfony when the code is working you are doing it right. \$\endgroup\$ Nov 26, 2014 at 12:51

1 Answer 1

2
\$\begingroup\$

Some notes:

  1. Try to keep your symfony installation free from persisting or removing data and write everything else to your database. So you need not to save any paths or provide routes for the upload folder. It's just another action returning upload data from the database. If you use version control and deploy a new version you will see how great it is, to have the filesystem readonly and the database as the whole state of the application.
  2. Your Post class is not responsible for doing any uploading operations.
  3. Keep your controller action as small as possible, since that code is not very reusable. Write services and use them everywhere.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.