I have installed Sonata Media Bundle in Symfony 4 and all is correct, but something is different respect t Symfony 3.
I can't see the service in Sonata Admin and when I add the sonata media bundle field to an Admin Class this shows a different template.
Here images:
Sonata Media Bundle template - Symfony 4, in User Entity
Sonata Media Bundle template - Symfony 3, in User Entity
Sonata Media Bundle template - Symfony 3, Adding new image
As you can see the template is not working in Symfony 4 or I'm missing something in my code.
My Sonata Media config
sonata_media.yaml
sonata_media:
class:
media: App\Application\Sonata\MediaBundle\Entity\Media
gallery: App\Application\Sonata\MediaBundle\Entity\Gallery
gallery_has_media: App\Application\Sonata\MediaBundle\Entity\GalleryHasMedia
default_context: default
contexts:
default:
providers:
- sonata.media.provider.dailymotion
- sonata.media.provider.youtube
- sonata.media.provider.image
- sonata.media.provider.file
- sonata.media.provider.vimeo
formats:
small: { width: 100 , quality: 70}
big: { width: 500 , quality: 70}
cdn:
server:
path: /upload/media
filesystem:
local:
# Directory for uploads should be writable
directory: "%kernel.project_dir%/public/upload/media"
create: false
providers:
# ...
file:
# the resizer must be set to false, otherwhise this can delete icon files from the fs
resizer: false
image:
thumbnail: sonata.media.thumbnail.format # default value
# thumbnail: sonata.media.thumbnail.consumer.format # can be used to dispatch the resize action to async task
# thumbnail: sonata.media.thumbnail.liip_imagine # use the LiipImagineBundle to resize the image
vimeo:
thumbnail: sonata.media.thumbnail.format # default value
# thumbnail: sonata.media.thumbnail.consumer.format # can be used to dispatch the resize action to async task
# thumbnail: sonata.media.thumbnail.liip_imagine # use the LiipImagineBundle to resize the image
youtube:
thumbnail: sonata.media.thumbnail.format # default value
# thumbnail: sonata.media.thumbnail.consumer.format # can be used to dispatch the resize action to async task
# thumbnail: sonata.media.thumbnail.liip_imagine # use the LiipImagineBundle to resize the image
dailymotion:
thumbnail: sonata.media.thumbnail.format # default value
# thumbnail: sonata.media.thumbnail.consumer.format # can be used to dispatch the resize action to async task
# thumbnail: sonata.media.thumbnail.liip_imagine # use the LiipImagineBundle to resize the image
My User's Admin Class
// src/Admin/OgaUsersAdmin.php
namespace App\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\MediaBundle\Form\Type\MediaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
class OgaUsersAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper->add('userFirstName', TextType::class)
->add('userCollection', MediaType::class, array(
'provider' => 'sonata.media.provider.image',
'context' => 'default'
));
}
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper->add('userFirstName');
}
protected function configureListFields(ListMapper $listMapper)
{
$listMapper->addIdentifier('userFirstName');
}
}
My Users Entity and Media Bundle field
namespace App\Entity;
use Application\Sonata\MediaBundle\Entity\Media;
use Doctrine\ORM\Mapping as ORM;
/**
* OgaUsers
*
* #ORM\Table(name="oga_users", indexes={#ORM\Index(name="memb_id_idx", columns={"memb_id"}), #ORM\Index(name="comp_id_idx", columns={"comp_id"}), #ORM\Index(name="u_ui_id_idx", columns={"user_collection"})})
* #ORM\Entity
*/
class OgaUsers
{
/**
* #var int
*
* #ORM\Column(name="user_id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $userId;
/**
* #var Media
*
* #ORM\ManyToOne(targetEntity="App\Application\Sonata\MediaBundle\Entity\Media")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="userCollection", referencedColumnName="id")
* })
*/
private $userCollection;
Getter and Settter
public function getUserCollection(): ?\App\Application\Sonata\MediaBundle\Entity\Media
{
return $this->userCollection;
}
public function setUserCollection(?\App\Application\Sonata\MediaBundle\Entity\Media $userCollection): self
{
$this->userCollection = $userCollection;
return $this;
}
Thank's
After search for some weeks.
I have the answer.
First I need to add this line to my sonata_media.yaml, to see the Media Library in Admin Dashboard.
db_driver: doctrine_orm # or doctrine_mongodb, doctrine_phpcr it is mandatory to choose one here
After in Admin in configureFormFields, just need to add ModellistType::class, to the media field.
class OgaUsersAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper->add('userFirstname', TextType::class)
->add('userImage', ModelListType::class);
}
Hope it helps!!
Related
My problem is simple but complicated at the same time.
Basically when you upload a image with easy_admin. The image get's a hash.
Like so:
/uploads/images/5f17449f4932f_image004.jpg
Is there any way to remove the generated hash before the image name ?
Here is my Entity:
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* Image
*
* #ORM\Column(type="string", length=255)
* #var string
*/
private $image = '';
/**
* ImageFile
*
* #Vich\UploadableField(mapping="images", fileNameProperty="image")
* #var File
*/
private $imageFile;
Is there a setting that I may use in the easy_admin.yml config ?
form:
fields:
- { property: 'imageFile', label: 'Image', type: 'vich_image'}
Let me know if any other information is needed.
Thank you.
UPDATE: The class
namespace App\Service\FileNamer;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Vich\UploaderBundle\Mapping\PropertyMapping;
use Vich\UploaderBundle\Naming\NamerInterface;
class FileNamer implements NamerInterface
{
public function name($object, PropertyMapping $mapping): string
{
/* #var $file UploadedFile */
$file = $mapping->getFile($object);
return $file->getClientOriginalName();
}
}
Easy_admin
db_driver: orm
mappings:
images:
uri_prefix: '%upload_images_folder%'
upload_destination: '%kernel.root_dir%/../public%upload_images_folder%'
namer:
service: App\Service\FileNamer
videos:
uri_prefix: '%upload_videos_folder%'
upload_destination: '%kernel.root_dir%/../public%upload_videos_folder%'
namer:
service: vich_uploader.namer_origname
pdfs:
uri_prefix: '%upload_pdfs_folder%'
upload_destination: '%kernel.root_dir%/../public%upload_pdfs_folder%'
namer:
service: vich_uploader.namer_origname
Create your own custom namer class. Just implement Vich\UploaderBundle\Naming\NamerInterface and add it to vich_uploader configuration.
https://github.com/dustin10/VichUploaderBundle/blob/master/docs/file_namer/howto/create_a_custom_file_namer.md
I have a symfony 4 project with a User entity that has a relationship with an Avatar entity (images uploaded with VichUploaderBundle).
In Avatar.php:
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $imageName;
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* #Assert\Image(
* mimeTypes="image/jpeg")
* #Vich\UploadableField(mapping="avatar", fileNameProperty="imageName", size="imageSize")
*
* #var File|null
*/
private $imageFile;
In User.php:
/**
* #ORM\OneToOne(targetEntity="App\Entity\Avatar", mappedBy="user", cascade={"persist", "remove"})
*/
private $avatar;
I have a profile page to edit a user's data (name, surname, mail, avatar).
In this page, I use LiipImagineBundle to display the current avatar in a certain dimension.
When the user edits his profile, I wish a listener can check is there are changes in the avatar. In which case, it deletes the old media / cache.
So I created a Listener for that:
<?php
namespace App\Listener;
use App\Entity\Avatar;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Vich\UploaderBundle\Templating\Helper\UploaderHelper;
class ImageCacheSubscriber implements EventSubscriber
{
/**
* CacheManager
*
* #var CacheManager
*/
private $cacheManager;
/**
* UploaderHelper
*
* #var UploaderHelper
*/
private $uploaderHelper;
public function __construct(CacheManager $cacheManager, UploaderHelper $uploaderHelper)
{
$this->cacheManager = $cacheManager;
$this->uploaderHelper = $uploaderHelper;
}
public function getSubscribedEvents()
{
return [
'preRemove',
'preUpdate'
];
}
public function preRemove(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if (!$entity instanceof Avatar) {
return;
}
$this->cacheManager->remove($this->uploaderHelper->asset($entity, 'imageFile'));
}
public function preUpdate(PreUpdateEventArgs $args)
{
dump($args->getEntity());
dump($args->getObject());
$entity = $args->getEntity();
if (!$entity instanceof Avatar) {
return;
}
if ($entity->getImageFile() instanceof UploadedFile) {
$this->cacheManager->remove($this->uploaderHelper->asset($entity, 'imageFile'));
}
}
}
Services.yaml:
App\Listener\ImageCacheSubscriber:
tags:
- { name: doctrine.event_subscriber }
But when I change my avatar, the listener removes the entire folder containing the avatars in the media.
And he provokes me this error:
Failed to remove directory
"C:\Users\user\Desktop\congesTest2/public/media/cache/avatar_big\files":
rmdir(C:\Users\user\Desktop\congesTest2/public/media/cache/avatar_big\files):
Directory not empty.
I don't understand why ... :'(
EDIT:
I update my function preUpdate() to postUpdate() :
public function getSubscribedEvents()
{
return [
'preRemove',
'postUpdate'
];
}
public function postUpdate(LifecycleEventArgs $args)
{
dump($args->getEntity());
$entity = $args->getEntity();
if (!$entity instanceof Avatar) {
return;
}
if ($entity->getImageFile() instanceof UploadedFile) {
$this->cacheManager->remove($this->uploaderHelper->asset($entity, 'imageFile'));
}
}
And now, if I make a dump of :
dd($this->uploaderHelper->asset($entity, 'imageFile'));
I've :
"/images/avatar/avatar3.jpg"
And this is the good path ! On the other hand, the image is not removed from the cache! The remove () function does not seem to give anything it's amazing
With the dump of the entity, I saw that the file was no longer an UploadedFile but a File simply. Whereas before it seemed like an UploadedFile. So I changed the line
if ($entity->getImageFile() instanceof UploadedFile)
by
if ($entity->getImageFile() instanceof File)
But the image is still not deleted from the cache.
In my opinion, since this is a postUpdate, it removes the new cache image, not the old one. But since the user is redirected to the same page, he delivers it immediately after caching. (EDIT : No I did a test, the image is not even removed from the cache)
Instead of listening to Doctrine events, you can listen to the vich_uploader.pre_remove event. This will ensure you get the old image that needs to be removed every time. First, make sure your VichUploader config is set to delete files on update and remove. This is the default.
# config/packages/vich_uploader.yaml
vich_uploader:
mappings:
avatar:
upload_destination: '%kernel.project_dir%/public/uploads/avatar'
uri_prefix: 'uploads/avatar'
delete_on_update: true
delete_on_remove: true
Now you need to create the listener.
// src/EventListener/ImageCacheSubscriber.php
namespace App\EventListener;
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Vich\UploaderBundle\Event\Event;
use Vich\UploaderBundle\Event\Events;
use Vich\UploaderBundle\Storage\StorageInterface;
class ImageCacheSubscriber implements EventSubscriberInterface
{
private $storage;
private $cacheManager;
public function __construct(StorageInterface $storage, CacheManager $cacheManager)
{
$this->storage = $storage;
$this->cacheManager = $cacheManager;
}
public function onRemove(Event $event)
{
$path = $this->storage->resolveUri($event->getObject(), $event->getMapping()->getFilePropertyName());
$this->cacheManager->remove($path);
}
public static function getSubscribedEvents()
{
return [Events::PRE_REMOVE => 'onRemove'];
}
}
When any VichUploader asset is removed, this listener will attempt to remove it from the cache for all filters. You can specify specific filters in the CacheManager::remove() method if you would like. You could also only remove the cache for specific entities by checking the instance of $event->getObject().
This also makes a few assumptions about your LiipImagine config. If you're using the default loader and cache resolver, this should work. If you're using a custom loader or resolver, you may need to modify this listener to your needs.
# config/packages/liip_imagine.yaml
liip_imagine:
resolvers:
default:
web_path:
web_root: '%kernel.project_dir%/public'
cache_prefix: 'media/cache'
loaders:
default:
filesystem:
data_root:
- '%kernel.project_dir%/public'
filter_sets:
cache: ~
# Your filters...
If you're using Symfony Flex, you're done. Otherwise, make sure to register the listener as a service.
# config/services.yaml
services:
# ...
App\EventListener\ImageCacheSubscriber:
arguments: ['#vich_uploader.storage.file_system', '#liip_imagine.cache.manager']
tags:
- { name: kernel.event_subscriber }
As you are using VichUploaderBundle it should not be needed to create your own listener. Make sure the lifecycle events are properly configured and Vich should take care of deleting old images.
# config/packages/vich_uploader.yaml or app/config/config.yml
vich_uploader:
db_driver: orm
mappings:
product_image:
uri_prefix: /images/products
upload_destination: '%kernel.project_dir%/public/images/products'
inject_on_load: false
delete_on_update: true
delete_on_remove: true
https://github.com/dustin10/VichUploaderBundle/blob/master/Resources/doc/usage.md#step-3-configure-the-lifecycle-events-optional-step
I have entity Asset with field image
/**
* #ORM\Table(name="asset")
* #ORM\Entity(repositoryClass="AppBundle\Repository\AssetRepository")
* #Vich\Uploadable
*/
class Asset
{
/**
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="assets")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
/**
* #ORM\Column(name="image", type="string", length=255, nullable=true)
*/
private $image;
/**
* #Vich\UploadableField(mapping="assets", fileNameProperty="image")
* #var File
*/
protected $imageFile;
I am using VichUploader to upload images to S3 bucket. I created custom namer to create file name. During upload entity is uploaded to folder with category name and is named with entity name
public function name($obj, PropertyMapping $mapping)
{
$category = $obj->getCategory()->getName();
$name = $obj->getName();
$file = $mapping->getFile($obj);
if ($extension = $this->getExtension($file)) {
$name = sprintf('%s.%s', $name, $extension);
}
return $category.'/'.$name;
}
These my upload configurations
oneup_flysystem:
adapters:
assets_adapter:
awss3v3:
client: app.assets.s3
bucket: '%assets_bucket%'
prefix: assets
filesystems:
assets_fs:
adapter: assets_adapter
mount: assets_fs
vich_uploader:
db_driver: orm
storage: flysystem
mappings:
assets:
delete_on_remove: true
delete_on_update: true
uri_prefix: 'https://s3-%assets_region%.amazonaws.com/%assets_bucket%/'
upload_destination: assets_fs
namer: app.asset_namer
I have following situation: user changes category of Asset. How file can be re-uploaded to new category folder and update name?
UPDATE
I am using EasyAdminBundle. Which handles create and edit entities. So I didn't create FormType and Controller for Asset entity. Here are configs:
easy_admin:
entities:
Asset:
class: AppBundle\Entity\Asset
label: 'Assets'
form:
fields:
- name
- {property: 'category', type: entity, type_options: {expanded: false, multiple: false, class: 'AppBundle:Category', required: true}}
- {property: 'imageFile', type: 'vich_image' }
One solution would be to update the Asset::setCategory method logic, to create a new File object when the category name is changed, and pass it to Asset::setImageFile to cause VichUploader to run the update process.
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* #ORM\Table(name="asset")
* #ORM\Entity(repositoryClass="AppBundle\Repository\AssetRepository")
* #Vich\Uploadable
*/
class Asset
{
//...
public function setCategory(Category $category)
{
if ($this->imageFile && $this->category->getName() !== $category->getName()) {
$this->setImageFile(new UploadedFile(
$this->imageFile->getRealPath(), //physical path to image file
$this->image, //current image name
null,
null,
null,
true
));
}
$this->category = $category;
return $this;
}
}
One issue is that by default VichUploader does not populate the entity imageFile property.
To ensure Asset::$imageFile is available, without needing to interact with the vich_uploader.storage service, you will need to add inject_on_load: true to your vich_uploader.mappings settings. This will add a listener to the entity to automatically populate the imageFile property with a File object.
#app/config/config.yml
#...
vich_uploader:
db_driver: orm
storage: flysystem
mappings:
assets:
delete_on_remove: true
delete_on_update: true
uri_prefix: 'https://s3-%assets_region%.amazonaws.com/%assets_bucket%/'
upload_destination: assets_fs
namer: app.asset_namer
inject_on_load: true
The prefered method would be to add the logic to your controller edit action, or adding a custom event subscriber to a Symfony event that monitors the Asset entity. Though I am not aware of how to accomplish this in relation to a category name change in EasyAdminBundle.
I use symfony2 and SonataAdminBundle, SonataMediaBundle and SonataClassificationBundle
Now I want custmize setting for admin panel, but I have this error.
[Symfony\Component\Config\Exception\FileLoaderLoadException]
Catchable Fatal Error: Argument 5 passed to Sonata\MediaBundle\Admin\BaseMe
diaAdmin::__construct() must implement interface Sonata\ClassificationBundl
e\Model\CategoryManagerInterface, none given, called in /Users/whitebear/Codin
gWorks/httproot/myapp/app/cache/de_/appDevDebugProjectContaine_.php on l
ine 9494 and defined in . (which is being imported from "/Users/whitebear/Codi
ngWorks/httproot/myapp/app/config/routing.yml").
What I have done is two things.
made DependencyInjection file
Application/Sonata/MediaBundle/DependencyInjection/ApplicationSonataMediaExtension.php
<?php
namespace Application\Sonata\MediaBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
/**
* This is the class that loads and manages your bundle configuration
*
* To learn more see {#link http://symfony.com/doc/current/cookbook/bundles/extension.html}
*/
class ApplicationSonataMediaExtension extends Extension
{
/**
* {#inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
}
}
then made services.yml for admin
Application/Sonata/MediaBundle/Resources/config/services.yml
#Disable gallery & media menu from admin panel
services:
sonata.media.admin.media:
class: %sonata.media.admin.media.class%
tags:
- { name: sonata.admin, manager_type: orm, show_in_dashboard: false, label_catalogue: %sonata.media.admin.media.translation_domain% , label_translator_strategy: sonata.admin.label.strategy.underscore }
arguments:
- ~
- %sonata.media.admin.media.entity%
- %sonata.media.admin.media.controller%
- "#sonata.media.pool"
- %sonata.classification.manager.category% # add here.
calls:
- [setModelManager, ["#sonata.media.admin.media.manager"]]
- [setTranslationDomain, [%sonata.media.admin.media.translation_domain%]]
- [setTemplates, [{ inner_list_row: SonataMediaBundle:MediaAdmin:inner_row_media.html.twig , base_list_field: SonataAdminBundle:CRUD:base_list_flat_field.html.twig , list: SonataMediaBundle:MediaAdmin:list.html.twig , edit: SonataMediaBundle:MediaAdmin:edit.html.twig }]]
sonata.media.admin.gallery:
class: %sonata.media.admin.gallery.class%
tags:
- { name: sonata.admin, manager_type: orm, show_in_dashboard: false, label_catalogue: %sonata.media.admin.media.translation_domain% , label_translator_strategy: sonata.admin.label.strategy.underscore }
arguments:
- ~
- %sonata.media.admin.gallery.entity%
- %sonata.media.admin.gallery.controller%
- "#sonata.media.pool"
calls:
- [setTranslationDomain, [%sonata.media.admin.media.translation_domain%]]
- [setTemplates, [{ list: SonataMediaBundle:GalleryAdmin:list.html.twig }]]
in Sonata\MediaBundle\Admin\BaseMediaAdmin
abstract class BaseMediaAdmin extends AbstractAdmin
{
/**
* #var Pool
*/
protected $pool;
/**
* #var CategoryManagerInterface
*/
protected $categoryManager;
/**
* #param string $code
* #param string $class
* #param string $baseControllerName
* #param Pool $pool
* #param CategoryManagerInterface $categoryManager
*/
public function __construct($code, $class, $baseControllerName, Pool $pool, CategoryManagerInterface $categoryManager)
{
parent::__construct($code, $class, $baseControllerName);
$this->pool = $pool;
$this->categoryManager = $categoryManager;
}
Thanks to #mdma
I figured out I must path 5th parameters as CategoryManagerInterface to BaseMediaAdmin constructor.
then I updated the service.yml but I have error like this.
[Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException]
The service "sonata.media.admin.media" has a dependency on a non-existent p
arameter "sonata.classification.manager.category". Did you mean one of thes
e: "sonata.classification.manager.category.class", "sonata.classification.m
anager.tag.class", "sonata.classification.manager.context.class", "sonata.c
lassification.manager.tag.entity", "sonata.classification.manager.category.
entity", "sonata.classification.manager.context.entity", "sonata.classifica
tion.admin.category.class"?
It solved.
I changed this sentence inservices.yml
- %sonata.classification.manager.category% to "#sonata.classification.manager.category"
The error say : Argument 5 doesn't exist in Sonata\MediaBundle\Admin\BaseMediaAdmin::__construct()
So, look at arguments in you sonata.media.admin.media service configuration. There are only 4 arguments. You need to add the 5th.
In bundle config (https://github.com/sonata-project/SonataMediaBundle/blob/master/Resources/config/doctrine_orm_admin.xml), there are 5 arguments :
<argument/>
<argument>%sonata.media.admin.media.entity%</argument>
<argument>%sonata.media.admin.media.controller%</argument>
<argument type="service" id="sonata.media.pool"/>
<argument type="service" id="sonata.media.manager.category" on-invalid="null"/>
So, I think you can add #sonata.media.manager.category as 5th argument.
i try setup translations form
http://a2lix.fr/bundles/translation-form/ and https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/translatable.md#translatable-entity-example
composer.json
"a2lix/translation-form-bundle": "2.*#dev",
"stof/doctrine-extensions-bundle": "1.2.*#dev",
config.yml
stof_doctrine_extensions:
default_locale: en
orm:
default:
translatable: true
sluggable: true
sluggable: true
timestampable: true
a2lix_translation_form:
locale_provider: default # [1]
locales: [pl, en, de] # [1-a]
default_locale: en
manager_registry: doctrine # [2]
templating: "A2lixTranslationFormBundle::default.html.twig"
entity
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use APY\DataGridBundle\Grid\Mapping as GRID;
use Gedmo\Mapping\Annotation as Gedmo;
use Gedmo\Translatable\Translatable;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="c_Base"
,indexes={
* #ORM\Index(name="search_name", columns={"name"}),
* #ORM\Index(name="orderCity", columns={"city"})
* })
*/
class Base implements Translatable{
/**
* #ORM\Column(type="bigint")
* #ORM\Id
*/
private $id;
/**
* Hexaid
* #var string
*/
private $hid;
/**
* #ORM\Column(type="string")
* #GRID\Column(title="name")
* #Gedmo\Translatable
* #var string
*/
private $name;
/**
* #Gedmo\Locale
* Used locale to override Translation listener`s locale
* this is not a mapped field of entity metadata, just a simple property
*/
private $locale;
build form
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('translations', 'a2lix_translations', array(
'fields'=>array(
'name'=>array(),
'description'=>array(
'field_type' => 'ckeditor'
)
)
)
);
error
Neither the property "translations" nor one of the methods "getTranslations()", "translations()", "isTranslations()", "hasTranslations()", "__get()" exist and have public access in class "Mea\CharterBundle\Entity\Base".
i dont't have private $translations; var in base - because is public translation - in example for personal translations exist $translations
https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/translatable.md#personal-translations
but for https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/translatable.md#translatable-entity-example not.
Can i use it in http://a2lix.fr/bundles/translation-form/ ?
here is other way example
DoctrineExtensions Notice: Undefined index: foreignKey in $em->getRepository('Gedmo\\Translatable\\Entity\\Translation');
Here is the working example from my current project how to use DoctrineExtension from KNP (I know this doesn't answer the question, look at the comments above).
composer.json:
"knplabs/doctrine-behaviors": "dev-master",
"a2lix/translation-form-bundle" : "dev-master"
config.yml:
imports:
...
- { resource: ../../vendor/knplabs/doctrine-behaviors/config/orm-services.yml }
framework:
translator:
fallback: "%locale%"
...
a2lix_translation_form:
locale_provider: default
locales: [ru, en]
default_locale: ru
required_locales: [ru]
manager_registry: doctrine
templating: "#SLCore/includes/translation.html.twig" # if you want to use your own template
Entities:
Product
use Knp\DoctrineBehaviors\Model\Translatable\Translatable;
class Product
{
use Translatable;
// other fields which should not be translated
// $translations field alredy has been created and mapped by DE
ProductTranslation (DE requires this entity):
use Knp\DoctrineBehaviors\Model\Translatable\Translation;
class ProductTranslation
{
use Translation;
// fields that should be translated
// e.g. $title, $description (id, locale, translatable fields already have been created by Translation trait, mapped by DE)
ProductType form:
->add('translations', 'a2lix_translations', [/* other options go here */])
form.html.twig:
{% block javascripts %}
{{ parent() }}
<script type="text/javascript" src="{{ asset('bundles/a2lixtranslationform/js/a2lix_translation_bootstrap.js') }}"></script>
{% endblock %}
...
{{ form_widget(form.translations) }}
I know this is not an interesting example, but it works.