I'm trying to create two custom providers, one for PDF and another one for ZIP files.
This is my code.
services.yml
parameters:
application_sonata_media.zip_class: Application\Sonata\MediaBundle\Provider\ZipProvider
services:
sonata.media.provider.zip:
class: %application_sonata_media.zip_class%
tags:
- { name: sonata.media.provider }
arguments:
- sonata.media.provider.zip
- #sonata.media.filesystem.local
- #sonata.media.cdn.server
- #sonata.media.generator.default
- #sonata.media.thumbnail.format
- ['zip']
- ['application/zip']
sonata_media.yml
sonata_media:
contexts:
eag_zip:
download:
strategy: sonata.media.security.public_strategy
mode: http
providers:
- sonata.media.provider.zip
formats:
reference: { quality: 100 }
ZipProvider.php
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
namespace Application\Sonata\MediaBundle\Provider;
use Sonata\MediaBundle\Provider\BaseProvider;
use Gaufrette\Filesystem;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Validator\ErrorElement;
use Sonata\CoreBundle\Model\Metadata;
use Sonata\MediaBundle\CDN\CDNInterface;
use Sonata\MediaBundle\Generator\GeneratorInterface;
use Sonata\MediaBundle\Metadata\MetadataBuilderInterface;
use Sonata\MediaBundle\Model\MediaInterface;
use Sonata\MediaBundle\Thumbnail\ThumbnailInterface;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\NotNull;
/**
* Description of ZipProvider
*
* #author Juanjo García <juanjogarcia#editartgroup.com>
*/
class ZipProvider extends BaseProvider {
protected $allowedExtensions;
protected $allowedMimeTypes;
protected $metadata;
/**
* #param string $name
* #param \Gaufrette\Filesystem $filesystem
* #param \Sonata\MediaBundle\CDN\CDNInterface $cdn
* #param \Sonata\MediaBundle\Generator\GeneratorInterface $pathGenerator
* #param \Sonata\MediaBundle\Thumbnail\ThumbnailInterface $thumbnail
* #param array $allowedExtensions
* #param array $allowedMimeTypes
* #param \Sonata\MediaBundle\Metadata\MetadataBuilderInterface $metadata
*/
public function __construct($name, Filesystem $filesystem, CDNInterface $cdn, GeneratorInterface $pathGenerator, ThumbnailInterface $thumbnail, array $allowedExtensions = array(), array $allowedMimeTypes = array(), MetadataBuilderInterface $metadata = null)
{
parent::__construct($name, $filesystem, $cdn, $pathGenerator, $thumbnail);
$this->allowedExtensions = $allowedExtensions;
$this->allowedMimeTypes = $allowedMimeTypes;
$this->metadata = $metadata;
}
/**
* {#inheritdoc}
*/
public function getProviderMetadata()
{
return new Metadata($this->getName(), $this->getName().'.description', false, 'SonataMediaBundle', array('class' => 'fa fa-file-text-o'));
}
/**
* {#inheritdoc}
*/
public function getReferenceImage(MediaInterface $media)
{
return sprintf('%s/%s',
$this->generatePath($media),
$media->getProviderReference()
);
}
/**
* {#inheritdoc}
*/
public function getReferenceFile(MediaInterface $media)
{
return $this->getFilesystem()->get($this->getReferenceImage($media), true);
}
/**
* {#inheritdoc}
*/
public function buildEditForm(FormMapper $formMapper)
{
$formMapper->add('name');
$formMapper->add('enabled', null, array('required' => false));
$formMapper->add('authorName');
$formMapper->add('cdnIsFlushable');
$formMapper->add('description');
$formMapper->add('copyright');
$formMapper->add('binaryContent', 'file', array('required' => false));
}
/**
* {#inheritdoc}
*/
public function buildCreateForm(FormMapper $formMapper)
{
$formMapper->add('binaryContent', 'file', array(
'constraints' => array(
new NotBlank(),
new NotNull(),
),
));
}
/**
* {#inheritdoc}
*/
public function buildMediaType(FormBuilder $formBuilder)
{
$formBuilder->add('binaryContent', 'file');
}
/**
* {#inheritdoc}
*/
public function postPersist(MediaInterface $media)
{
if ($media->getBinaryContent() === null) {
return;
}
$this->setFileContents($media);
$this->generateThumbnails($media);
}
/**
* {#inheritdoc}
*/
public function postUpdate(MediaInterface $media)
{
if (!$media->getBinaryContent() instanceof \SplFileInfo) {
return;
}
// Delete the current file from the FS
$oldMedia = clone $media;
$oldMedia->setProviderReference($media->getPreviousProviderReference());
$path = $this->getReferenceImage($oldMedia);
if ($this->getFilesystem()->has($path)) {
$this->getFilesystem()->delete($path);
}
$this->fixBinaryContent($media);
$this->setFileContents($media);
$this->generateThumbnails($media);
}
/**
* #throws \RuntimeException
*
* #param \Sonata\MediaBundle\Model\MediaInterface $media
*
* #return
*/
protected function fixBinaryContent(MediaInterface $media)
{
if ($media->getBinaryContent() === null) {
return;
}
// if the binary content is a filename => convert to a valid File
if (!$media->getBinaryContent() instanceof File) {
if (!is_file($media->getBinaryContent())) {
throw new \RuntimeException('The file does not exist : '.$media->getBinaryContent());
}
$binaryContent = new File($media->getBinaryContent());
$media->setBinaryContent($binaryContent);
}
}
/**
* #throws \RuntimeException
*
* #param \Sonata\MediaBundle\Model\MediaInterface $media
*/
protected function fixFilename(MediaInterface $media)
{
if ($media->getBinaryContent() instanceof UploadedFile) {
$media->setName($media->getName() ?: $media->getBinaryContent()->getClientOriginalName());
$media->setMetadataValue('filename', $media->getBinaryContent()->getClientOriginalName());
} elseif ($media->getBinaryContent() instanceof File) {
$media->setName($media->getName() ?: $media->getBinaryContent()->getBasename());
$media->setMetadataValue('filename', $media->getBinaryContent()->getBasename());
}
// this is the original name
if (!$media->getName()) {
throw new \RuntimeException('Please define a valid media\'s name');
}
}
/**
* {#inheritdoc}
*/
protected function doTransform(MediaInterface $media)
{
$this->fixBinaryContent($media);
$this->fixFilename($media);
// this is the name used to store the file
if (!$media->getProviderReference()) {
$media->setProviderReference($this->generateReferenceName($media));
}
if ($media->getBinaryContent()) {
$media->setContentType($media->getBinaryContent()->getMimeType());
$media->setSize($media->getBinaryContent()->getSize());
}
$media->setProviderStatus(MediaInterface::STATUS_OK);
}
/**
* {#inheritdoc}
*/
public function updateMetadata(MediaInterface $media, $force = true)
{
// this is now optimized at all!!!
$path = tempnam(sys_get_temp_dir(), 'sonata_update_metadata');
$fileObject = new \SplFileObject($path, 'w');
$fileObject->fwrite($this->getReferenceFile($media)->getContent());
$media->setSize($fileObject->getSize());
}
/**
* {#inheritdoc}
*/
public function generatePublicUrl(MediaInterface $media, $format)
{
if ($format == 'reference') {
$path = $this->getReferenceImage($media);
} else {
// #todo: fix the asset path
$path = sprintf('sonatamedia/files/%s/file.png', $format);
}
return $this->getCdn()->getPath($path, $media->getCdnIsFlushable());
}
/**
* {#inheritdoc}
*/
public function getHelperProperties(MediaInterface $media, $format, $options = array())
{
return array_merge(array(
'title' => $media->getName(),
'thumbnail' => $this->getReferenceImage($media),
'file' => $this->getReferenceImage($media),
), $options);
}
/**
* {#inheritdoc}
*/
public function generatePrivateUrl(MediaInterface $media, $format)
{
if ($format == 'reference') {
return $this->getReferenceImage($media);
}
return false;
}
/**
* Set the file contents for an image.
*
* #param \Sonata\MediaBundle\Model\MediaInterface $media
* #param string $contents path to contents, defaults to MediaInterface BinaryContent
*/
protected function setFileContents(MediaInterface $media, $contents = null)
{
$file = $this->getFilesystem()->get(sprintf('%s/%s', $this->generatePath($media), $media->getProviderReference()), true);
if (!$contents) {
$contents = $media->getBinaryContent()->getRealPath();
}
$metadata = $this->metadata ? $this->metadata->get($media, $file->getName()) : array();
$file->setContent(file_get_contents($contents), $metadata);
}
/**
* #param \Sonata\MediaBundle\Model\MediaInterface $media
*
* #return string
*/
protected function generateReferenceName(MediaInterface $media)
{
return sha1($media->getName().rand(11111, 99999)).'.'.$media->getBinaryContent()->guessExtension();
}
/**
* {#inheritdoc}
*/
public function getDownloadResponse(MediaInterface $media, $format, $mode, array $headers = array())
{
// build the default headers
$headers = array_merge(array(
'Content-Type' => $media->getContentType(),
'Content-Disposition' => sprintf('attachment; filename="%s"', $media->getMetadataValue('filename')),
), $headers);
if (!in_array($mode, array('http', 'X-Sendfile', 'X-Accel-Redirect'))) {
throw new \RuntimeException('Invalid mode provided');
}
if ($mode == 'http') {
if ($format == 'reference') {
$file = $this->getReferenceFile($media);
} else {
$file = $this->getFilesystem()->get($this->generatePrivateUrl($media, $format));
}
return new StreamedResponse(function () use ($file) {
echo $file->getContent();
}, 200, $headers);
}
if (!$this->getFilesystem()->getAdapter() instanceof \Sonata\MediaBundle\Filesystem\Local) {
throw new \RuntimeException('Cannot use X-Sendfile or X-Accel-Redirect with non \Sonata\MediaBundle\Filesystem\Local');
}
$filename = sprintf('%s/%s',
$this->getFilesystem()->getAdapter()->getDirectory(),
$this->generatePrivateUrl($media, $format)
);
return new BinaryFileResponse($filename, 200, $headers);
}
/**
* {#inheritdoc}
*/
public function validate(ErrorElement $errorElement, MediaInterface $media)
{
if (!$media->getBinaryContent() instanceof \SplFileInfo) {
return;
}
if ($media->getBinaryContent() instanceof UploadedFile) {
$fileName = $media->getBinaryContent()->getClientOriginalName();
} elseif ($media->getBinaryContent() instanceof File) {
$fileName = $media->getBinaryContent()->getFilename();
} else {
throw new \RuntimeException(sprintf('Invalid binary content type: %s', get_class($media->getBinaryContent())));
}
if (!in_array(strtolower(pathinfo($fileName, PATHINFO_EXTENSION)), $this->allowedExtensions)) {
$errorElement
->with('binaryContent')
->addViolation('Invalid extensions')
->end();
}
if (!in_array($media->getBinaryContent()->getMimeType(), $this->allowedMimeTypes)) {
$errorElement
->with('binaryContent')
->addViolation('Invalid mime type : '.$media->getBinaryContent()->getMimeType())
->end();
}
}
}
When I push create and edit button, the file pass the validation, save in DDBB and file in server, but in URL /admin/sonata/media/media/233/edit?provider=sonata.media.provider.zip&context=eag_zip I get this Error message:
Unable to find template "" in SonataMediaBundle:MediaAdmin:edit.html.twig at line 48.
Trying to go to /admin/sonata/media/media/list?provider=sonata.media.provider.zip&context=eag_zip or /admin/sonata/media/media/list?context=eag_zip
Unable to find template "" in SonataMediaBundle:MediaAdmin:inner_row_media.html.twig at line 19.
I don't know to solve it.
any help would be welcome
thanks a lot
Solved here
services:
sonata.media.provider.zip:
class: %application_sonata_media.zip_class%
tags:
- { name: sonata.media.provider }
arguments:
- sonata.media.provider.zip
- #sonata.media.filesystem.local
- #sonata.media.cdn.server
- #sonata.media.generator.default
- #sonata.media.thumbnail.format
- ['zip']
- ['application/zip']
calls:
- [ setTemplates, [{helper_view:SonataMediaBundle:Provider:view_image.html.twig,helper_thumbnail:SonataMediaBundle:Provider:thumbnail.html.twig}]]
Related
I have a custom Symfony 4 deserializer
class CardImageDecoder implements EncoderInterface, DecoderInterface
{
public function encode($data, $format, array $context = [])
{
if($format !== 'json') {
throw new EncodingFormatNotSupportedException(sprintf('Format %s is not supported by encoder %s', $format, __CLASS__));
}
$result = json_encode($data);
if(json_last_error() !== JSON_ERROR_NONE) {
// don't bother with a custom error message
throw new \Exception(sprintf('Unable to encode data, got error message: %s', json_last_error_msg()));
}
return $result;
}
public function supportsEncoding($format)
{
return 'json' === $format;
}
public function decode($data, $format, array $context = [])
{
if($format !== 'array') {
throw new DecodingFormatNotSupportedException(sprintf('Format %s is not supported by encoder %s', $format, __CLASS__));
}
if(!is_array($data)) {
throw new \UnexpectedValueException(sprintf('Expected array got %s', gettype($data)));
}
$cardInstance = new CardImages();
$cardInstance->setHeight($data['h'] ?? 0);
$cardInstance->setPath($data['url'] ?? '');
$cardInstance->setWidth($data['w'] ?? 0);
return $cardInstance;
}
public function supportsDecoding($format)
{
return 'array' === $format;
}
}
The way I'm deserializing is pretty straight forward:
$json = '
{
"url": "some url",
"h": 1004,
"w": 768
}';
$encoders = [new CardImageDecoder()];
$normalizers = [new ObjectNormalizer()];
$serializer = new Serializer($normalizers, $encoders);
$cardImage = $serializer->deserialize(json_decode($json, true), CardImages::class, 'array');
/** #var $cardImage CardImages */
var_dump($cardImage);
However, I get this result returned:
object(App\Entity\CardImages)#158 (5) {
["id":"App\Entity\CardImages":private]=>
NULL
["path":"App\Entity\CardImages":private]=>
NULL
["height":"App\Entity\CardImages":private]=>
NULL
["width":"App\Entity\CardImages":private]=>
NULL
["movie":"App\Entity\CardImages":private]=>
NULL
}
Now, if I were to do a dump, just before the return in the decode part of the decoder, I'd get this:
...
$cardInstance->setWidth($data['w'] ?? 0);
var_dump($cardInstance);
object(App\Entity\CardImages)#153 (5) {
["id":"App\Entity\CardImages":private]=>
NULL
["path":"App\Entity\CardImages":private]=>
string(8) "some url"
["height":"App\Entity\CardImages":private]=>
int(1004)
["width":"App\Entity\CardImages":private]=>
int(768)
["movie":"App\Entity\CardImages":private]=>
NULL
}
Ignoring the not set properties(which I'm fine with), it should work quite nicely, but it doesn't.
For the life of me I can't figure out what's wrong.
Any help is appreciated.
You are thinking way to complicated.
Tested, working example:
Your entity - take a closer look at the end - we need setters with the names of your array keys:
namespace App\Domain;
class CardImages
{
/** #var int|null */
private $id;
/** #var int|null */
private $height;
/** #var string|null */
private $path;
/** #var int|null */
private $width;
/** #var mixed */
private $movie;
/**
* #return int|null
*/
public function getId(): ?int
{
return $this->id;
}
/**
* #param int|null $id
* #return CardImages
*/
public function setId(?int $id): CardImages
{
$this->id = $id;
return $this;
}
/**
* #return int|null
*/
public function getHeight(): ?int
{
return $this->height;
}
/**
* #param int|null $height
* #return CardImages
*/
public function setHeight(?int $height): CardImages
{
$this->height = $height;
return $this;
}
/**
* #return string|null
*/
public function getPath(): ?string
{
return $this->path;
}
/**
* #param string|null $path
* #return CardImages
*/
public function setPath(?string $path): CardImages
{
$this->path = $path;
return $this;
}
/**
* #return int|null
*/
public function getWidth(): ?int
{
return $this->width;
}
/**
* #param int|null $width
* #return CardImages
*/
public function setWidth(?int $width): CardImages
{
$this->width = $width;
return $this;
}
/**
* #return mixed
*/
public function getMovie()
{
return $this->movie;
}
/**
* #param mixed $movie
* #return CardImages
*/
public function setMovie($movie)
{
$this->movie = $movie;
return $this;
}
public function setH(?int $height): CardImages
{
$this->setHeight($height);
return $this;
}
public function setUrl(?string $url): CardImages
{
$this->setPath($url);
return $this;
}
public function setW(?int $width): CardImages
{
$this->setWidth($width);
return $this;
}
}
Simple Controller to test the Output:
<?php
namespace App\Infrastructure\Web\Controller;
use App\Domain\CardImages;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\SerializerInterface;
/**
* Class IndexController.
*
* #Route("/test")
*/
class TestController extends AbstractController
{
private $serializer;
public function __construct(SerializerInterface $serializer)
{
$this->serializer = $serializer;
}
/**
* #Route("")
*
* #return Response
*/
public function indexAction(): Response
{
$data = [
[
'h' => 100,
'w' => 100,
'path' => '/asdf/asdf.png',
],
[
'h' => 50,
'w' => 150,
'path' => '/asdf/foo.png',
],
[
'h' => 100,
'w' => 200,
'path' => '/asdf/bar.png',
],
[
'h' => 300,
'w' => 400,
'path' => '/asdf/baz.png',
],
];
foreach ($data as $row) {
$cardImage = $this->serializer->denormalize($row, CardImages::class, null, [
ObjectNormalizer::DISABLE_TYPE_ENFORCEMENT => false
]);
$json = $this->serializer->serialize($cardImage, 'json');
dump($cardImage, $json);
}
return new Response('<html><head></head></html></body></html>');
}
}
Output:
I was following these guides: File upload tutorial and Collection form type guide. Everything was ok but "edit" action. Collection of images is shown correctly in "edit" action but when I try to submit the form (without adding any new images) i get an error.
Call to a member function guessExtension() on null
Which means that images in a collection got null values, instead of UploadedFile.
I've searched a lot and read many of sof questions about handling collection of images with doctrine but still no clue.
Questions like:
Symfony2, Edit Form, Upload Picture
howto handle edit forms with FileType inputs in symfony2
Multiple (oneToMany) Entities form generation with symfony2 and file upload
So the question is how can u handle edit images? Removal is needed too.
Controller edit action
public function editAction(Request $request, Stamp $stamp)
{
if (!$stamp) {
throw $this->createNotFoundException('No stamp found');
}
$form = $this->createForm(AdminStampForm::class, $stamp);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** #var Stamp $stamp */
$stamp = $form->getData();
foreach ($stamp->getImages() as $image) {
$fileName = md5(uniqid()) . '.' . $image->getName()->guessExtension();
/** #var $image StampImage */
$image->getName()->move(
$this->getParameter('stamps_images_directory'),
$fileName
);
$image->setName($fileName)->setFileName($fileName);
}
$em = $this->getDoctrine()->getManager();
$em->persist($stamp);
$em->flush();
$this->addFlash('success', 'Successfully edited a stamp!');
return $this->redirectToRoute('admin_stamps_list');
}
return [
'form' => $form->createView(),
];
}
Entity
<?php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\JoinTable;
use Doctrine\ORM\Mapping\ManyToMany;
/**
* Class Stamp
* #package AppBundle\Entity
*
*
* #ORM\Entity(repositoryClass="AppBundle\Repository\StampRepository")
* #ORM\Table(name="stamp")
* #ORM\HasLifecycleCallbacks()
*/
class Stamp
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
private $id;
// * #ORM\OneToMany(targetEntity="AppBundle\Entity\StampImage", mappedBy="id", cascade={"persist", "remove"})
/**
* #ManyToMany(targetEntity="AppBundle\Entity\StampImage", cascade={"persist"})
* #JoinTable(name="stamps_images",
* joinColumns={#JoinColumn(name="stamp_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="image_id", referencedColumnName="id", unique=true)}
* ) */
private $images;
/**
* #ORM\Column(type="datetime")
*/
private $createdAt;
/**
* #ORM\Column(type="datetime")
*/
private $updatedAt;
public function __construct()
{
$this->images = new ArrayCollection();
}
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
public function addImage(StampImage $image)
{
$this->images->add($image);
}
public function removeImage(StampImage $image)
{
$this->images->removeElement($image);
}
/**
* #return mixed
*/
public function getImages()
{
return $this->images;
}
/**
* #param mixed $images
*
* #return $this
*/
public function setImages($images)
{
$this->images = $images;
return $this;
}
/**
* #return mixed
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* #param mixed $createdAt
*
* #return Stamp
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* #return mixed
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* #param mixed $updatedAt
*
* #return Stamp
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* #ORM\PrePersist
* #ORM\PreUpdate
*/
public function updateTimestamps()
{
$this->setUpdatedAt(new \DateTime('now'));
if (null == $this->getCreatedAt()) {
$this->setCreatedAt(new \DateTime());
}
}
}
StampImage entity
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity as UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Class StampImage
* #package AppBundle\Entity
*
* #ORM\Entity()
* #ORM\Table(name="stamp_image")
* #UniqueEntity(fields={"name"}, message="This file name is already used.")
*/
class StampImage
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
private $id;
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
private $name;
/**
* #ORM\Column(type="string")
*/
private $fileName;
/**
* #return mixed
*/
public function getFileName()
{
return $this->fileName;
}
/**
* #param mixed $fileName
*
* #return StampImage
*/
public function setFileName($fileName)
{
$this->fileName = $fileName;
return $this;
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*
* #return StampImage
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
}
Main entity form
<?php
namespace AppBundle\Form;
use AppBundle\Entity\Stamp;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class AdminStampForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('images', CollectionType::class, [
'entry_type' => AdminStampImageForm::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Stamp::class,
]);
}
public function getName()
{
return 'app_bundle_admin_stamp_form';
}
}
Image form type
<?php
namespace AppBundle\Form;
use AppBundle\Entity\StampImage;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class AdminStampImageForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', FileType::class, [
'image_name' => 'fileName',
'label' => false,
'attr' => [
],
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => StampImage::class,
'required' => false
]);
}
public function getBlockPrefix()
{
return 'app_bundle_admin_stamp_image_form';
}
}
FileType extension
- extends 'form_div_layout.html.twig'
- block file_widget
- spaceless
- if image_url is not null
%img{:src => "#{image_url}"}
%div{:style => "display: none;"}
#{block('form_widget')}
- else
#{block('form_widget')}
1) Multiupload (but not OneToMany).
2) For editing an uploaded image:
# AppBundle/Entity/Stamp
/**
* #ORM\Column(type="string", length=255)
*/
private $image;
# AppBundle/Form/StampForm
->add('imageFile', FileType::class, [ //the $image property of Stamp entity class will store the path to the file, and this imageFile field will get the uploaded file
'data_class' => null, //important!
])
#AppBundle/Controller/StampController
/**
* #Route("/{id}/edit", name="stamp_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Stamp $stamp) {
$editForm = $this->createForm(StampType::class, $stamp, [
'action'=>$this->generateUrl('stamp_edit',['id'=>$stamp->getId()]),
'method'=>'POST'
]);
$editForm->handleRequest($request);
if($editForm->isSubmitted() && $form->isValid()) {
$imageFile = $editForm->get('imageFile')->getData();
if (null != $imageFile) { //this means that for the current record that needs to be edited, the user has chosen a different image
//1. remove the old image
$oldImg = $this->getDoctrine()->getRepository('AppBundle:Stamp')->find($stamp);
$this->get('app.file_remove')->removeFile($oldImg->getImage());
//2. upload the new image
$img = $this->get('app.file_upload')->upload($imageFile);
//3. update the db, replacing the path to the old file with the path to the new uploaded file
$stamp->setImage($img);
$this->getDoctrine()->getManager()->flush();
//4. add a success flash, and anything else you need, and redirect to a route
} else { //if the user has chosen to edit a different field (but not the image one)
$this->getDoctrine()->getManager()->flush();
//add flash message, and redirect to a route
}
}
return $this->render(...);
}
#AppBundle/Services/FileRemove
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
class FileRemove {
private $targetDir;
public function __construct($targetDir) {
$this->targetDir = $targetDir;
}
public function removeFile($path) {
$fs = new Filesystem();
$file = $this->targetDir . '/' . $path;
try{
if($fs->exists($file)){
$fs->remove($file);
return true;
}
return false;
} catch(IOExceptionInterface $e){
//log error for $e->getPath();
}
}
}
#app/config/services.yml
app.file_remove:
class: AppBundle/Services/FileRemove
arguments: ['%stamp_dir%']
#app/config/config.yml
parameters:
stamp_dir: '%kernel.root_dir%/../web/uploads/stamps' //assuming this is how you've set up the upload directory
#AppBundle/Services/FileUpload
use Symfony\Component\HttpFoundation\File\UploadedFile;
class FileUpload{
private $targetDir;
public function __construct($targetDir) {
$this->targetDir = $targetDir;
}
public function upload(UploadedFile $file) {
$file_name = empty($file->getClientOriginalName()) ? md5(uniqid()).'.'.$file->guessExtension() : $file->getClientOriginalName();
$file->move($this->targetDir, $file_name);
return $file_name;
}
}
#app/config/services.yml
app.file_upload:
class: AppBundle\Services\FileUpload
arguments: ['%stamp_dir%']
Sorry for typos, and please let me know if this works for your case, as I adapted my case to yours.
Check you have uploaded image vie form or not before foreach.
if ($form->isSubmitted() && $form->isValid()) {
/** #var Stamp $stamp */
$stamp = $form->getData();
if(!empty($stamp->getImages()) && count($stamp->getImages()) > 0){ // Check uploaded image
foreach ($stamp->getImages() as $image) {
$fileName = md5(uniqid()) . '.' . $image->guessExtension();
/** #var $image StampImage */
$image->getName()->move(
$this->getParameter('stamps_images_directory'),
$fileName
);
$image->setName($fileName)->setFileName($fileName);
}
}
$em = $this->getDoctrine()->getManager();
$em->persist($stamp);
$em->flush();
$this->addFlash('success', 'Successfully edited a stamp!');
return $this->redirectToRoute('admin_stamps_list');
}
Update #1
Replace
$fileName = md5(uniqid()) . '.' . $image->getName()->guessExtension();
with
$fileName = md5(uniqid()) . '.' . $image->guessExtension();
I'm trying to upload a file using my own form with symfony2.
I get no errors, and the name of the file is correctly addeded on database, but the file is not uploaded.
here is my routing.yml :
upload_homepage:
pattern: /upload
defaults: { _controller: UploadBundle:Default:index }
upload:
pattern: /upload/file
defaults: { _controller: UploadBundle:Default:upload }
twig:
<form enctype="multipart/form-data" action="{{ path('upload') }}" method="POST">
<input type="file" name="file">
<input type="submit">
</form>
My controller :
<?php
namespace Upload\UploadBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Upload\UploadBundle\Entity\Document;
class DefaultController extends Controller
{
public function indexAction()
{
return $this->render('UploadBundle:Default:index.html.twig');
}
public function uploadAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$document = new Document();
$document->setChemain($request->files->get('file')->getClientOriginalName());
//$document->upload();
// print_r($request->files->get('file')->getClientOriginalName());
// die();
$em->persist($document);
$em->flush();
return new Response("Ok");
}
}
The Entity:
<?php
namespace Upload\UploadBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
// use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* Document
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Upload\UploadBundle\Entity\DocumentRepository")
* #ORM\HasLifecycleCallbacks
*/
class Document
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="chemain", type="string", length=255)
*/
private $chemain;
/**
* #Assert\File(maxSize="6000000")
* mimeTypes = {"image/jpg", "image/gif", "image/png", "image/tiff"},
* maxSizeMessage = "The maxmimum allowed file size is 5MB.",
* mimeTypesMessage = "Only the filetypes image are allowed."
*/
public $file;
/**
* Get id
*
* #return integer
*/
// propriété utilisé temporairement pour la suppression
private $filenameForRemove;
public function getId()
{
return $this->id;
}
/**
* Set chemain
*
* #param string $chemain
* #return Document
*/
public function setChemain($chemain)
{
$this->chemain = $chemain;
return $this;
}
/**
* Get chemain
*
* #return string
*/
public function getChemain()
{
return $this->chemain;
}
public function getWebPath()
{
return null === $this->chemain ? null : $this->getUploadDir().'/'.$this->chemain;
}
protected function getUploadRootDir()
{
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
protected function getUploadDir()
{
return 'uploads';
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if (null !== $this->file) {
$this->chemain = $this->file->guessExtension();
}
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
if (null === $this->file) {
return;
}
try {
$this->file->move($this->getUploadRootDir(), $this->id.'.'.$this->file->guessExtension());
unset($this->file);
} catch (FileException $e) {
return $e;
}
}
/**
* #ORM\PreRemove()
*/
public function storeFilenameForRemove()
{
$this->filenameForRemove = $this->getAbsolutePath();
}
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
if ($this->filenameForRemove) {
unlink($this->filenameForRemove);
}
}
public function getAbsolutePath()
{
return null === $this->chemain ? null : $this->getUploadRootDir().'/'.$this->id.'.'.$this->chemain;
}
}
Any idea ?
I find a solution, it's work, but I don't know if is the correctly way to do this.
the solution : I modify just the function uploadAction
public function uploadAction(Request $request)
{
$document = new Document();
$form = $this->createFormBuilder($document)
->add('file')
->getForm();
if ($this->getRequest()->isMethod('POST')) {
$form->bind($this->getRequest());
$em = $this->getDoctrine()->getManager();
$em->persist($document);
$em->flush();
return new Response("Ok");
}
return new Response("No");
}
You must uncoment upload line
namespace Upload\UploadBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Upload\UploadBundle\Entity\Document;
class DefaultController extends Controller
{
public function indexAction()
{
return $this->render('UploadBundle:Default:index.html.twig');
}
public function uploadAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$document = new Document();
$document->upload();
$em->persist($document);
$em->flush();
return new Response("Ok");
}
}
And at yours document entity add upload method
/**
* #param $varfile
*
* #return $this
*/
public function setFile($file = null)
{
$this->file = $file;
return $this;
}
/**
* #return string $file
*/
public function getFile()
{
return $this->file;
}
public function upload()
{
// the file property can be empty if the field is not required
if (null === $this->getFile()) {
return;
}
// use the original file name here but you should
// sanitize it at least to avoid any security issues
// move takes the target directory and then the
// target filename to move to
$this->getFile()->move(
$this->getUploadRootDir(),
$this->getFile()->getClientOriginalName()
);
// set the path property to the filename where you've saved the file
$this->file = $this->getFile()->getClientOriginalName();
$this->chemain = $this->getFile()->getClientOriginalName();
}
/**
* #return null|string
*/
public function getAbsolutePath()
{
if($this->file)
{
return $this->getUploadRootDir().'/'.$this->file;
}
return null;
}
/**
* #return null|string
*/
public function getWebPath()
{
if($this->file)
{
return '/web/'.$this->getUploadDir().'/'.$this->file;
}
return null;
}
In my Restful API, i want to upload a file in one call.
In my tests, the form is initialized and binded at the same time, but all my data fields form are empty and the result is an empty record in my database.
If I pass by the form view and then submit it, all is fine but i want to call the Webservice in one call. The webservice is destinated to be consumed by a backbone app.
Thanks for your help.
My Test:
$client = static::createClient();
$photo = new UploadedFile(
'/Userdirectory/test.jpg',
'photo.jpg',
'image/jpeg',
14415
);
$crawler = $client->request('POST', '/ws/upload/mydirectory', array(), array('form[file]' => $photo), array('Content-Type'=>'multipart/formdata'));
There is my controller action:
public function uploadAction(Request $request, $directory, $_format)
{
$document = new Media();
$document->setDirectory($directory);
$form = $this->createFormBuilder($document, array('csrf_protection' => false))
/*->add('directory', 'hidden', array(
'data' => $directory
))*/
->add('file')
->getForm()
;
if ($this->getRequest()->isMethod('POST')) {
$form->bind($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($document);
$em->flush();
if($document->getId() !== '')
return $this->redirect($this->generateUrl('media_show', array('id'=>$document->getId(), 'format'=>$_format)));
}else{
$response = new Response(serialize($form->getErrors()), 406);
return $response;
}
}
return array('form' => $form->createView());
}
My Media Entity:
<?php
namespace MyRestBundle\RestBundle\Entity;
use Symfony\Component\Serializer\Normalizer\NormalizableInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Media
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
protected $path;
public $directory;
/**
* #Assert\File(maxSize="6000000")
*/
public $file;
/**
* #see \Symfony\Component\Serializer\Normalizer\NormalizableInterface
*/
function normalize(NormalizerInterface $normalizer, $format= null)
{
return array(
'path' => $this->getPath()
);
}
/**
* #see
*/
function denormalize(NormalizerInterface $normalizer, $data, $format = null)
{
if (isset($data['path']))
{
$this->setPath($data['path']);
}
}
protected function getAbsolutePath()
{
return null === $this->path ? null : $this->getUploadRootDir().'/'.$this->path;
}
protected 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 when displaying uploaded doc/image in the view.
return 'uploads/'.(null === $this->directory ? 'documents' : $this->directory);
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if (null !== $this->file) {
// do whatever you want to generate a unique name
$this->path = $this->getUploadDir().'/'.sha1(uniqid(mt_rand(), true)).'.'.$this->file->guessExtension();
}
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
if (null === $this->file) {
return;
}
// if there is an error when moving the file, an exception will
// be automatically thrown by move(). This will properly prevent
// the entity from being persisted to the database on error
$this->file->move($this->getUploadRootDir(), $this->path);
unset($this->file);
}
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
if ($file = $this->getAbsolutePath()) {
unlink($file);
}
}
/**
* Set Directory
*
* #param string $directory
* #return Media
*/
public function setDirectory($directory)
{
$this->directory = $directory;
return $this;
}
/**
* Set Path
*
* #param string $path
* #return Media
*/
public function setPath($path)
{
$this->path = $path;
return $this;
}
/**
* Get path
*
* #return string
*/
public function getPath()
{
$request = Request::createFromGlobals();
return $request->getHost().'/'.$this->path;
}
/**
* Get id
*
* #return string
*/
public function getId()
{
return $this->id;
}
}
My routing:
upload_dir_media:
pattern: /upload/{directory}.{_format}
defaults: { _controller: MyRestBundle:Media:upload, _format: html }
requirements: { _method: POST }
Try breaking this problem down into a simple state. How would you post 'text' or a variable to a web service with one post? Because an image is just a long string. Check out the php function imagecreatefromstring or imgtostring . This is often what goes on behind the scenes of you image transfer protocols.. Once you so solve the simpler problem, you will have proven you can solve your original problem.
Do anyone know why this occurs?
as far I can get, the child class method is declared in the same way as parent's.
Thanks!
here is my kernel code:
<?php
require_once __DIR__.'/../src/autoload.php';
use Symfony\Framework\Kernel;
use Symfony\Components\DependencyInjection\Loader\YamlFileLoader as ContainerLoader;
use Symfony\Components\Routing\Loader\YamlFileLoader as RoutingLoader;
use Symfony\Framework\KernelBundle;
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\ZendBundle\ZendBundle;
use Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle;
use Symfony\Bundle\DoctrineBundle\DoctrineBundle;
use Symfony\Bundle\DoctrineMigrationsBundle\DoctrineMigrationsBundle;
use Symfony\Bundle\DoctrineMongoDBBundle\DoctrineMongoDBBundle;
use Symfony\Bundle\PropelBundle\PropelBundle;
use Symfony\Bundle\TwigBundle\TwigBundle;
use Application\UfaraBundle\UfaraBundle;
class UfaraKernel extends Kernel {
public function registerRootDir() {
return __DIR__;
}
public function registerBundles() {
$bundles = array(
new KernelBundle(),
new FrameworkBundle(),
new ZendBundle(),
new SwiftmailerBundle(),
new DoctrineBundle(),
//new DoctrineMigrationsBundle(),
//new DoctrineMongoDBBundle(),
//new PropelBundle(),
//new TwigBundle(),
new UfaraBundle(),
);
if ($this->isDebug()) {
}
return $bundles;
}
public function registerBundleDirs() {
$bundles = array(
'Application' => __DIR__.'/../src/Application',
'Bundle' => __DIR__.'/../src/Bundle',
'Symfony\\Framework' => __DIR__.'/../src/vendor/symfony/src/Symfony/Framework',
'Symfony\\Bundle' => __DIR__.'/../src/vendor/symfony/src/Symfony/Bundle',
);
return $bundles;
}
public function registerContainerConfiguration(LoaderInterface $loader) {
return $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
}
public function registerRoutes() {
$loader = new RoutingLoader($this->getBundleDirs());
return $loader->load(__DIR__.'/config/routing.yml');
}
}
here is the parent class code:
<?php
namespace Symfony\Framework;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
use Symfony\Component\DependencyInjection\Resource\FileResource;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\Loader\DelegatingLoader;
use Symfony\Component\DependencyInjection\Loader\LoaderResolver;
use Symfony\Component\DependencyInjection\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Framework\ClassCollectionLoader;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier#symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* The Kernel is the heart of the Symfony system. It manages an environment
* that can host bundles.
*
* #author Fabien Potencier <fabien.potencier#symfony-project.org>
*/
abstract class Kernel implements HttpKernelInterface, \Serializable
{
protected $bundles;
protected $bundleDirs;
protected $container;
protected $rootDir;
protected $environment;
protected $debug;
protected $booted;
protected $name;
protected $startTime;
protected $request;
const VERSION = '2.0.0-DEV';
/**
* Constructor.
*
* #param string $environment The environment
* #param Boolean $debug Whether to enable debugging or not
*/
public function __construct($environment, $debug)
{
$this->environment = $environment;
$this->debug = (Boolean) $debug;
$this->booted = false;
$this->rootDir = realpath($this->registerRootDir());
$this->name = basename($this->rootDir);
if ($this->debug) {
ini_set('display_errors', 1);
error_reporting(-1);
$this->startTime = microtime(true);
} else {
ini_set('display_errors', 0);
}
}
public function __clone()
{
if ($this->debug) {
$this->startTime = microtime(true);
}
$this->booted = false;
$this->container = null;
$this->request = null;
}
abstract public function registerRootDir();
abstract public function registerBundles();
abstract public function registerBundleDirs();
abstract public function registerContainerConfiguration(LoaderInterface $loader);
/**
* Checks whether the current kernel has been booted or not.
*
* #return boolean $booted
*/
public function isBooted()
{
return $this->booted;
}
/**
* Boots the current kernel.
*
* This method boots the bundles, which MUST set
* the DI container.
*
* #throws \LogicException When the Kernel is already booted
*/
public function boot()
{
if (true === $this->booted) {
throw new \LogicException('The kernel is already booted.');
}
if (!$this->isDebug()) {
require_once __DIR__.'/bootstrap.php';
}
$this->bundles = $this->registerBundles();
$this->bundleDirs = $this->registerBundleDirs();
$this->container = $this->initializeContainer();
// load core classes
ClassCollectionLoader::load(
$this->container->getParameter('kernel.compiled_classes'),
$this->container->getParameter('kernel.cache_dir'),
'classes',
$this->container->getParameter('kernel.debug'),
true
);
foreach ($this->bundles as $bundle) {
$bundle->setContainer($this->container);
$bundle->boot();
}
$this->booted = true;
}
/**
* Shutdowns the kernel.
*
* This method is mainly useful when doing functional testing.
*/
public function shutdown()
{
$this->booted = false;
foreach ($this->bundles as $bundle) {
$bundle->shutdown();
$bundle->setContainer(null);
}
$this->container = null;
}
/**
* Reboots the kernel.
*
* This method is mainly useful when doing functional testing.
*
* It is a shortcut for the call to shutdown() and boot().
*/
public function reboot()
{
$this->shutdown();
$this->boot();
}
/**
* Gets the Request instance associated with the master request.
*
* #return Request A Request instance
*/
public function getRequest()
{
return $this->request;
}
/**
* Handles a request to convert it to a response by calling the HttpKernel service.
*
* #param Request $request A Request instance
* #param integer $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST)
* #param Boolean $raw Whether to catch exceptions or not
*
* #return Response $response A Response instance
*/
public function handle(Request $request = null, $type = HttpKernelInterface::MASTER_REQUEST, $raw = false)
{
if (false === $this->booted) {
$this->boot();
}
if (null === $request) {
$request = $this->container->get('request');
} else {
$this->container->set('request', $request);
}
if (HttpKernelInterface::MASTER_REQUEST === $type) {
$this->request = $request;
}
$response = $this->container->getHttpKernelService()->handle($request, $type, $raw);
$this->container->set('request', $this->request);
return $response;
}
/**
* Gets the directories where bundles can be stored.
*
* #return array An array of directories where bundles can be stored
*/
public function getBundleDirs()
{
return $this->bundleDirs;
}
/**
* Gets the registered bundle names.
*
* #return array An array of registered bundle names
*/
public function getBundles()
{
return $this->bundles;
}
/**
* Checks if a given class name belongs to an active bundle.
*
* #param string $class A class name
*
* #return Boolean true if the class belongs to an active bundle, false otherwise
*/
public function isClassInActiveBundle($class)
{
foreach ($this->bundles as $bundle) {
$bundleClass = get_class($bundle);
if (0 === strpos($class, substr($bundleClass, 0, strrpos($bundleClass, '\\')))) {
return true;
}
}
return false;
}
/**
* Returns the Bundle name for a given class.
*
* #param string $class A class name
*
* #return string The Bundle name or null if the class does not belongs to a bundle
*/
public function getBundleForClass($class)
{
$namespace = substr($class, 0, strrpos($class, '\\'));
foreach (array_keys($this->getBundleDirs()) as $prefix) {
if (0 === $pos = strpos($namespace, $prefix)) {
return substr($namespace, strlen($prefix) + 1, strpos($class, 'Bundle\\') + 7);
}
}
}
public function getName()
{
return $this->name;
}
public function getSafeName()
{
return preg_replace('/[^a-zA-Z0-9_]+/', '', $this->name);
}
public function getEnvironment()
{
return $this->environment;
}
public function isDebug()
{
return $this->debug;
}
public function getRootDir()
{
return $this->rootDir;
}
public function getContainer()
{
return $this->container;
}
public function getStartTime()
{
return $this->debug ? $this->startTime : -INF;
}
public function getCacheDir()
{
return $this->rootDir.'/cache/'.$this->environment;
}
public function getLogDir()
{
return $this->rootDir.'/logs';
}
protected function initializeContainer()
{
$class = $this->getSafeName().ucfirst($this->environment).($this->debug ? 'Debug' : '').'ProjectContainer';
$location = $this->getCacheDir().'/'.$class;
$reload = $this->debug ? $this->needsReload($class, $location) : false;
if ($reload || !file_exists($location.'.php')) {
$this->buildContainer($class, $location.'.php');
}
require_once $location.'.php';
$container = new $class();
$container->set('kernel', $this);
return $container;
}
public function getKernelParameters()
{
$bundles = array();
foreach ($this->bundles as $bundle) {
$bundles[] = get_class($bundle);
}
return array_merge(
array(
'kernel.root_dir' => $this->rootDir,
'kernel.environment' => $this->environment,
'kernel.debug' => $this->debug,
'kernel.name' => $this->name,
'kernel.cache_dir' => $this->getCacheDir(),
'kernel.logs_dir' => $this->getLogDir(),
'kernel.bundle_dirs' => $this->bundleDirs,
'kernel.bundles' => $bundles,
'kernel.charset' => 'UTF-8',
'kernel.compiled_classes' => array(),
),
$this->getEnvParameters()
);
}
protected function getEnvParameters()
{
$parameters = array();
foreach ($_SERVER as $key => $value) {
if ('SYMFONY__' === substr($key, 0, 9)) {
$parameters[strtolower(str_replace('__', '.', substr($key, 9)))] = $value;
}
}
return $parameters;
}
protected function needsReload($class, $location)
{
if (!file_exists($location.'.meta') || !file_exists($location.'.php')) {
return true;
}
$meta = unserialize(file_get_contents($location.'.meta'));
$time = filemtime($location.'.php');
foreach ($meta as $resource) {
if (!$resource->isUptodate($time)) {
return true;
}
}
return false;
}
protected function buildContainer($class, $file)
{
$parameterBag = new ParameterBag($this->getKernelParameters());
$container = new ContainerBuilder($parameterBag);
foreach ($this->bundles as $bundle) {
$bundle->registerExtensions($container);
if ($this->debug) {
$container->addObjectResource($bundle);
}
}
if (null !== $cont = $this->registerContainerConfiguration($this->getContainerLoader($container))) {
$container->merge($cont);
}
$container->freeze();
foreach (array('cache', 'logs') as $name) {
$dir = $container->getParameter(sprintf('kernel.%s_dir', $name));
if (!is_dir($dir)) {
if (false === #mkdir($dir, 0777, true)) {
die(sprintf('Unable to create the %s directory (%s)', $name, dirname($dir)));
}
} elseif (!is_writable($dir)) {
die(sprintf('Unable to write in the %s directory (%s)', $name, $dir));
}
}
// cache the container
$dumper = new PhpDumper($container);
$content = $dumper->dump(array('class' => $class));
if (!$this->debug) {
$content = self::stripComments($content);
}
$this->writeCacheFile($file, $content);
if ($this->debug) {
$container->addObjectResource($this);
// save the resources
$this->writeCacheFile($this->getCacheDir().'/'.$class.'.meta', serialize($container->getResources()));
}
}
protected function getContainerLoader(ContainerInterface $container)
{
$resolver = new LoaderResolver(array(
new XmlFileLoader($container, $this->getBundleDirs()),
new YamlFileLoader($container, $this->getBundleDirs()),
new IniFileLoader($container, $this->getBundleDirs()),
new PhpFileLoader($container, $this->getBundleDirs()),
new ClosureLoader($container),
));
return new DelegatingLoader($resolver);
}
/**
* Removes comments from a PHP source string.
*
* We don't use the PHP php_strip_whitespace() function
* as we want the content to be readable and well-formatted.
*
* #param string $source A PHP string
*
* #return string The PHP string with the comments removed
*/
static public function stripComments($source)
{
if (!function_exists('token_get_all')) {
return $source;
}
$output = '';
foreach (token_get_all($source) as $token) {
if (is_string($token)) {
$output .= $token;
} elseif (!in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
$output .= $token[1];
}
}
// replace multiple new lines with a single newline
$output = preg_replace(array('/\s+$/Sm', '/\n+/S'), "\n", $output);
// reformat {} "a la python"
$output = preg_replace(array('/\n\s*\{/', '/\n\s*\}/'), array(' {', ' }'), $output);
return $output;
}
protected function writeCacheFile($file, $content)
{
$tmpFile = tempnam(dirname($file), basename($file));
if (false !== #file_put_contents($tmpFile, $content) && #rename($tmpFile, $file)) {
chmod($file, 0644);
return;
}
throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file));
}
public function serialize()
{
return serialize(array($this->environment, $this->debug));
}
public function unserialize($data)
{
list($environment, $debug) = unserialize($data);
$this->__construct($environment, $debug);
}
}
Your answer lies in the imported namespaces. In the Kernel's file, there's this use clause:
use Symfony\Component\DependencyInjection\Loader\LoaderInterface;
So that ties LoaderInterface to the fully namespaced class Symfony\Component\DependencyInjection\Loader\LoaderInterface.
Basically making the signature:
public function registerContainerConfiguration(Symfony\Component\DependencyInjection\Loader\LoaderInterface $loader);
In your class, you don't import that namespace. So PHP by default assumes the class is in your namespace (since none of the imported namespaces have that interface name).
So your signature is (since you don't declare a namespace):
public function registerContainerConfiguration(\LoaderInterface $loader);
So to get them to match, simply add the use line to the top of your file:
use Symfony\Component\DependencyInjection\Loader\LoaderInterface;