I'm new to symfony, and I'm currently trying to understand Symfony3.
While validation through annotations works fine, I can't activate validation through validation.yml file.
In my app/config/config.yml, I changed this line :
framework:
validation: { enabled: true, enable_annotations: false }
My src/AppBundle/Resources/config/validation.yml is :
# src/AppBundle/Resources/config/validation.yml
AppBundle\Entity\TaskSetClass:
properties:
taskName:
- NotBlank: ~
dueDate:
- NotBlank: ~
- Type: \DateTime
I added a extension file :
<?php
// src/AppBundle/DependencyInjection/AppExtension.php
namespace AppBundle\DependencyInjection;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Finder\Finder;
/* */
class AppExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$container=new ContainerBuilder();
// Services
$loader=new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../../../app/config'));
$loader->load('services.yml');
// Validation
// Commented : No "validator.mapping.loader.yaml_files_loader.mapping_files" parameter.
//$yamlMappingFiles=$container->getParameter('validator.mapping.loader.yaml_files_loader.mapping_files');
$finder=new Finder();
foreach($finder->files()->in(__DIR__.'/../../../app/Resources/config') as $file)
{
$filePath=$file->getRealPath();
if(preg_match('#\.yml$#',$filePath)===1)
{
$yamlMappingFiles[]=$filePath;
}
}
$container->setParameter('validator.mapping.loader.yaml_files_loader.mapping_files', $yamlMappingFiles);
}
}
A dump of the container after the setParameter() in appExtension.php shows the command has succeeded :
[parameterBag:protected] => Symfony\Component\DependencyInjection\ParameterBag\ParameterBag Object
(
[parameters:protected] => Array
(
[validator.mapping.loader.yaml_files_loader.mapping_files] => Array
(
[0] => /mnt/400Go/www/sy1/app/Resources/config/validation.yml
)
)
[resolved:protected] =>
)
In my form controller, if I list the constraints :
[...]
if($form->isSubmitted())
{
if($form->isValid())
{
$metadata=$this->container
->get('validator')
->getMetadataFor('AppBundle\Entity\TaskSetClass');
$propertiesMetadata=$metadata->properties;
$constraints=array();
foreach ($propertiesMetadata as $propertyMetadata)
{
$constraints[$propertyMetadata->name]=$property->constraints;
}
echo'<pre>Constraints : ';
print_r($constraints);
echo'</pre>';
die();
[...]
... the array is empty. so the file seems to be loaded, but the constraints are ignored.
Did anyone encountered this error? It may comes from a difference between symfony 2 and 3.
[SOLVED] The validation file was not in the correct directory. It was supposed to be in AppBundle/Resources/config and I had it into app/Resouces/config. Works fine now.
Related
I just want to validate the extension of image that I upload in Sonata Admin (v3.28.0) with Sonata Media Bundle (v3.10.0) in Symfony (v2.8.32) application.
I've read all similar questions and Sonata documentations, but still don't have a success.
I tried to add constraints to config.yml
sonata_media:
providers:
image:
allowed_extensions:
- 'jpg'
- 'png'
I wonder that it doesn't work as is, because standard FileProvider (that is extended by ImageProvider) has extension check in validate method. But the method is not being called.
So I also tried to create custom provider:
services.yml:
sonata.media.provider.custom:
class: Application\Sonata\MediaBundle\Provider\CustomImageProvider
tags:
- { name: sonata.media.provider }
arguments:
- sonata.media.provider.custom
- #sonata.media.filesystem.local
- #sonata.media.cdn.server
- #sonata.media.generator.default
- #sonata.media.thumbnail.format
- ['jpg', 'png']
- ['image/pjpeg', 'image/jpeg', 'image/png', 'image/x-png']
- #sonata.media.adapter.image.imagick
- #sonata.media.metadata.proxy
calls:
- [ setTemplates, [{helper_view:SonataMediaBundle:Provider:view_image.html.twig,helper_thumbnail:SonataMediaBundle:Provider:thumbnail.html.twig}]]
Application\Sonata\MediaBundle\Provider\CustomImageProvider.php:
<?php
namespace Application\Sonata\MediaBundle\Provider;
use Sonata\CoreBundle\Validator\ErrorElement;
use Sonata\MediaBundle\Model\MediaInterface;
use Sonata\MediaBundle\Provider\ImageProvider;
class CustomImageProvider extends ImageProvider
{
public function validate(ErrorElement $errorElement, MediaInterface $media)
{
throw new \Exception();
}
}
config.yml:
sonata_media:
contexts:
image:
providers:
- sonata.media.provider.custom
formats:
small: { width: 100 , quality: 70}
big: { width: 500 , quality: 70}
But the validate method is still not being called.
So when I try to load GIF image I get an error:
Length of either side cannot be 0 or negative, current size is x
Do I miss something?
UPDATE
Simple validation can be added right in SomeEntityAdmin class like this:
public function validate(ErrorElement $errorElement, $object)
{
/** #var Media $image */
$image = $object->getImage();
if (!in_array($video->getContentType(), ['image/pjpeg', 'image/jpeg', 'image/png', 'image/x-png'])) {
$errorElement
->with('image')
->addViolation('Invalid file type')
->end()
;
};
}
But it's not a good solution if you want to validate a batch of uploaded images.
I started to learn Sylius few days ago. I need an ecommerce module within my Symfony 3 project. So I gonna extend my project with Sylius, I'll just use the Core/Components stack.
I started with the installation of the Sylius ProductBundle (cf. http://docs.sylius.org/en/latest/bundles/SyliusProductBundle/installation.html)
Seems simple but didn't work at first because Composer installed a stable version (0.19) which is not supported anymore... Anyway, I finally installed the sylius/sylius ~1.0#dev package so my composer.json looks like that :
"minimum-stability" : "dev",
"prefer-stable" : true,
"require" : {
"php" : "^7.0",
"sylius/sylius" : "^1.0#beta",
"friendsofsymfony/user-bundle" : "~2.0#dev",
"symfony/assetic-bundle" : "^2.8",
"gedmo/doctrine-extensions" : "^2.4"
},
=> First question : is this the right way ???
Then I created a SyliusBundle within my project to decouple the entire Sylius configuration from the rest of my project.
In SyliusBundle/app/config.yml :
imports:
- { resource: "#SyliusProductBundle/Resources/config/app/config.yml" }
# Doctrine
stof_doctrine_extensions:
default_locale: "%locale%"
orm:
default:
tree: true
sluggable: true
timestampable: true
loggable: false
sortable: true
# Product
sylius_product:
driver: doctrine/orm
resources:
product:
classes:
model: OfferBundle\Entity\Offer
repository: OfferBundle\Entity\OfferRepository
form: OfferBundle\Form\OfferType
I followed the Sylius guideline to customize my Offer model inherited from Sylius Product model (quite simple)
in OfferBundle/Entity/Offer.php :
<?php
namespace OfferBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Sylius\Component\Core\Model\Product as BaseProduct;
/**
* #ORM\Table(name="offer")
* #ORM\Entity(repositoryClass="OfferRepository")
*/
class Offer extends BaseProduct
{
=> question 2 : sounds good to you ?
Then I customized the form extended my Offer form with the Sylius Product form (cf. http://docs.sylius.org/en/latest/customization/form.html)
in OfferBundle/Form/OfferType.php :
<?php
namespace OfferBundle\Form;
use Symfony\Component\OptionsResolver\OptionsResolver;
use OfferBundle\Entity\Offer;
use Symfony\Component\Form\AbstractType;
class OfferType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class' => Offer::class]);
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'offer';
}
/**
* {#inheritdoc}
*/
public function getParent()
{
return \Sylius\Bundle\ProductBundle\Form\Type\ProductType::class;
}
}
In OfferBundle/Form/Extension/OfferTypeExtension.php :
<?php
namespace OfferBundle\Form\Extension;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use AppBundle\Form\Type\WysiwygType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use OfferBundle\Form\Type\TagsType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use OfferBundle\Entity\Offer;
use MediaBundle\Form\MediaFileType;
use MediaBundle\Form\MediaVideoType;
use CompanyBundle\Entity\Company;
use Tetranz\Select2EntityBundle\Form\Type\Select2EntityType;
use Admin\CompanyBundle\Controller\CompanyController;
use Symfony\Component\Form\AbstractTypeExtension;
use OfferBundle\Form\ContactType;
use OfferBundle\Form\PriceType;
use OfferBundle\Form\OfferType;
final class OfferTypeExtension extends AbstractTypeExtension
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('advantage', WysiwygType::class)
->add('ecommerce')
->add('enabled')
->add('bestseller')
->add('company', Select2EntityType::class, [
'multiple' => false,
'remote_route' => 'admin_company_company_autocomplete',
'class' => Company::class,
'primary_key' => 'id',
'text_property' => 'name',
'minimum_input_length' => 0,
'page_limit' => CompanyController::NB_PER_PAGE,
'allow_clear' => false,
'delay' => 250,
'cache' => false,
'cache_timeout' => 60000,
'language' => 'fr',
'placeholder' => 'form.choose',
'required' => true,
])
->add('state', ChoiceType::class, [
'choices' => Offer::getStates(),
'placeholder' => 'form.choose',
])
...
And the form extension is loaded by service.
in OfferBundle/Resources/config/services.yml :
offer.form.extension.type.offer:
class: OfferBundle\Form\Extension\OfferTypeExtension
tags:
- { name: form.type_extension, extended_type: OfferBundle\Form\OfferType }
Question 3 : It works BUT in see that Sylius\Bundle\CoreBundle\Form\Extension\ProductTypeExtension contains additionnal fields like $images, $variantSelectionMethod... But it doesn't appear in my form. So I suppose that a service.yml somewhere is not loaded ?
Hope somebody can help me on that !
I think the problem is because you have overridden the form, and not extended it. So you only see your form fields, and not the Product form fields (as this form is not used anymore).
If you want to have the Product form with some changes, you only have to make a form extension to modify the existing Product form.
http://docs.sylius.org/en/latest/customization/form.html
Hope this helps ! :)
you may also need to override the template of the form in order to show those added fields.
I'm using Symfony3 with the KnpGaufretteBundle to connect to an Amazon S3 bucket with the AWS S3 method outlined on their Github Readme
aws_s3_adapter:
key: "%aws_key%"
secret_key: "%aws_secret%"
region: "%aws_region%"
knp_gaufrette:
adapters:
images:
aws_s3:
service_id: 'aws_s3_adapter.client'
bucket_name: '%aws_bucket%'
options:
directory: 'images'
filesystems:
images:
adapter: images
alias: images_fs
I also have a service defined that I want to use to manage this filesystem (and others) with.
Definition:
services:
test.image_manager:
class: TestBundle\Filesystem\FileManager
arguments:
filesystem: "#images_fs"
filesystem_name: "images"
mimetypes: ["image/jpeg", "image/png", "image/gif"]
Class:
<?php
namespace TestBundle\Filesystem;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Gaufrette\Filesystem;
use Gaufrette\StreamWrapper;
class FileManager
{
private $allowedMimeTypes;
private $filesystem;
private $filsystem_name;
public function __construct(Filesystem $filesystem, $filesystem_name, $mimetypes = array())
{
$this->filesystem = $filesystem;
$this->filesystem_name = $filesystem_name;
$this->allowedMimeTypes = $mimetypes;
}
public function upload(UploadedFile $file, $filename)
{
// Check if the file's mime type is in the list of allowed mime types.
if (!in_array($file->getClientMimeType(), $this->allowedMimeTypes)) {
throw new \InvalidArgumentException(sprintf('Files of type %s are not allowed.', $file->getClientMimeType()));
}
$adapter = $this->filesystem->getAdapter();
$adapter->setMetadata($filename, array('contentType' => $file->getClientMimeType()));
return $adapter->write($filename, file_get_contents($file->getPathname()));
}
public function fetch( $filename )
{
if( ! $this->filesystem->has( $filename ) )
{
return false;
}
/* -- PROBLEM -- */
StreamWrapper::register();
return new BinaryFileResponse( "gaufrette://{$this->filesystem_name}/{$filename}" );
/* -- PROBLEM -- */
}
public function delete( $filename )
{
if( ! $this->filesystem->has( $filename ) )
{
return false;
}
return $this->filesystem->delete( $filename );
}
}
I'm able to upload successfully to the bucket using the upload function, telling me that the filesystem exists and is working properly.
I am not, however, able to use the Gaufrette\StreamWrapper to serve the file using a BinaryFileResponse as it says I should do. Instead it is giving me the error that I put in the title: There is no filesystem defined for the "images" domain.
The filesystem definitely exists, as I'm using it to upload the images. Any clues as to what the problem might be that's preventing me from using that filesystem would be very helpful. The Gaufrette documentation is really sparse online so far as I've found, but I'm going to keep digging.
Looking at MainConfiguration.php showed that there's a steam_wrapper option in the configuration for the bundle. I added this into my config.yml under where the filesystems are defined like so:
knp_gaufrette:
adapters:
images:
aws_s3:
service_id: 'aws_s3_adapter.client'
bucket_name: '%aws_bucket%'
options:
directory: 'images'
.
.
.
filesystems:
images:
adapter: images
alias: images_fs
.
.
.
stream_wrapper:
filesystems: [ images, ... ]
and the above code now works.
I refer this solution https://stackoverflow.com/a/15167450/2910183 ,and an error occur:
Compile Error: Declaration of
AppBundle\Block\NewsletterBlockService::execute() must be compatible
with
Sonata\BlockBundle\Block\BlockServiceInterface::execute(Sonata\BlockBundle\Block\BlockContextInterface
$blockContext, Symfony\Component\HttpFoundation\Response $response =
NULL)
Code
Part of my app/config/config.yml
sonata_block:
default_contexts: [cms]
blocks:
# enable the SonataAdminBundle block
sonata.admin.block.admin_list:
contexts: [admin]
sonata.user.block.menu: ~ # used to display the menu in profile pages
sonata.user.block.account: ~ # used to display menu option (login option)
sonata.block.service.text: ~
sonata.block.service.rss: ~
sonata.block.service.newsletter: ~
sonata_admin:
dashboard:
blocks:
-
position: left
type: sonata.admin.block.admin_list
settings:
groups: [default, app.admin.group.content]
-
position: right
type: sonata.admin.block.admin_list
settings:
groups: [default]
- { position: left, type: sonata.block.service.newsletter}
Part of my /private/var/www/learning_sonata/app/config/services.yml
sonata.block.service.newsletter:
class: AppBundle\Block\NewsletterBlockService
arguments: [ "sonata.block.service.newsletter", #templating ]
tags:
- { name: sonata.block }
My /private/var/www/learning_sonata/src/AppBundle/Block/NewsletterBlockService.php
namespace AppBundle\Block;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Sonata\BlockBundle\Model\BlockInterface;
use Sonata\BlockBundle\Block\BlockContextInterface;
use Sonata\BlockBundle\Block\BlockContext;
use Sonata\BlockBundle\Block\BlockServiceInterface;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\CoreBundle\Validator\ErrorElement;
use Sonata\BlockBundle\Block\BaseBlockService;
//use Sonata\BlockBundle\Block;
class NewsletterBlockService extends BaseBlockService
{
public function getName()
{
return 'My Newsletter';
}
public function getDefaultSettings()
{
return array();
}
public function validateBlock(ErrorElement $errorElement, BlockInterface $block)
{
}
public function buildEditForm(FormMapper $formMapper, BlockInterface $block)
{
}
public function execute(BlockInterface $block, Response $response = null)
{
// merge settings
$settings = array_merge($this->getDefaultSettings(), $block->getSettings());
return $this->renderResponse('InstitutoStoricoNewsletterBundle:Block:block_my_newsletter.html.twig', array(
'block' => $block,
'settings' => $settings
), $response);
}
}
Thanks devilcius!
I refer your answer,clear the error above,but new erro occur:
Method "id" for object "Sonata\BlockBundle\Block\BlockContext" does
not exist in SonataBlockBundle:Block:block_base.html.twig at line 11
File path vendor/sonata-project/block-bundle/Resources/views/Block/block_base.html.twig
<div id="cms-block-{{ block.id }}" class="cms-block cms-block-element">
{% block block %}EMPTY CONTENT{% endblock %}
</div>
Sonata Version
sonata-project/admin-bundle 3.1.0 The missing Symfony Admin ...
sonata-project/block-bundle 3.0.0 Symfony SonataBlockBundle
sonata-project/cache 1.0.7 Cache library
sonata-project/core-bundle 3.0.0 Symfony SonataCoreBundle
sonata-project/doctrine-orm-admin-bundle 3.0.1 Symfony Sonata / Integrate...
sonata-project/exporter 1.4.1 Lightweight Exporter library
I read this resource,it said it's the version pro.
Could u give some idea,guys?
You're not implementing the method signature when extending BaseBlockService.
use Sonata\BlockBundle\Block\BlockContextInterface;
public function execute(BlockContextInterface $blockContext, Response $response = null)
{
return $this->renderResponse('InstitutoStoricoNewsletterBundle:Block:block_my_newsletter.html.twig', array(
'block' => $blockContext->getBlock(),
'settings' => $blockContext->getSettings()
), $response);
}
I'm trying to upload one or more files using OneupUploaderBundle but I can not get it to work since files aren't uploaded and nothing is persisted to DB. I'll explain what I've done til now:
config.yml:
oneup_uploader:
mappings:
recaudos:
frontend: blueimp
storage:
service: ~
type: filesystem
filesystem: ~
directory: %kernel.root_dir%/../web/uploads/recaudos
stream_wrapper: ~
sync_buffer_size: 100K
allowed_mimetypes: [application/msword,image/jpeg,image/pjpeg,image/png,application/pdf,application/vnd.oasis.opendocument.text]
#disallowed_mimetypes: []
error_handler: oneup_uploader.error_handler.noop
# Set max_size to -1 for gracefully downgrade this number to the systems max upload size.
#max_size: 9223372036854775807
use_orphanage: true
enable_progress: true
enable_cancelation: true
namer: oneup_uploader.namer.uniqid
At Twig template only this:
<input id="fileUpload3" class="fileUpload" type="file" name="fileUpload3[]" data-url="{{ oneup_uploader_endpoint('recaudos') }}" multiple />
$(document).ready(function(){
$('fileUpload3').fileupload();
});
And latest this is the Listener for onUpload event:
namespace AppBundle\EventListener;
use Oneup\UploaderBundle\Event\PostPersistEvent;
use Symfony\Component\HttpFoundation\Request;
use AppBundle\Entity;
class UploadListener
{
protected $doctrine;
public function __construct($doctrine)
{
$this->doctrine = $doctrine;
}
public function onUpload(PostPersistEvent $event, Request $request)
{
$session = $request->getSession();
$em = $this->doctrine->getManager();
$request = $event->getRequest();
$tempfile = $event->getFile();
$productoSolicitud = $em->getRepository('SolicitudProducto')->find($session->get('productoSolicitudId'));
$recaudosTramiteProductoSolicitud = new Entity\RecaudosTramitesProductoSolicitud();
$recaudosTramiteProductoSolicitud->setProductoSolicitud($productoSolicitud);
$filenamePart = explode("--", $tempfile->getName());
$pathinfo = pathinfo($tempfile->getName());
$recaudosTramiteProductoSolicitud->setArchivo($pathinfo['filename']);
$em->persist($recaudosTramiteProductoSolicitud);
$em->flush();
}
}
And of course I have service defined also:
appbundle.upload_listener:
class: "AppBundle\EventListener\UploadListener"
arguments: [#doctrine]
tags:
- { name: 'kernel.event_listener', event: "oneup_uploader.post_persist", method: "onUpload" }
Did I miss something? Why file are not uploaded and data is not persisted? As you may see in the attached image there is a post request to _uploader/recaudos/upload, any advice?
I just checked my own OneupUploader implementation:
Rename your function "onUpload" to "onPostUpload" and change your service configuration to this:
appbundle.upload_listener:
class: "AppBundle\EventListener\UploadListener"
arguments: [#doctrine]
tags:
- { name: 'kernel.event_listener', event: oneup_uploader.post_upload, method: onPostUpload }
Should work, works for me.
If you need your original file name you also need an onUpload Method and this:
tags:
# - {...
- { name: kernel.event_listener, event: oneup_uploader.pre_upload, method: onUpload }
Regarding your comment:
//UploadListener
class UploadListener
{
protected $originalName;
public function onUpload(PreUploadEvent $event)
{
$file = $event->getFile();
$this->originalName = $file->getClientOriginalName();
}
public function onPostUpload(PostUploadEvent $event)
{
$fileEntity = new YourFileEntity();
$fileEntity->setYourOriginalNameProperty($this->originalName);
//...
}
}