Symfony and Bootstrap dateTimePicker : expected a string - php

I am trying to create a DateTimePickerType to easily add a Bootstrap dateTimePicker to a field, simply by using the type "date_time_picker" in the Form Builder.
Unfortunately I am running into some problems. Specifically, Symfony is giving me this error :
Symfony\Component\Validator\ConstraintViolation
Object(Symfony\Component\Form\Form).children[startDate] = Object(DateTime) - 2016-10-16T10:45:00+0200
Caused by:
Symfony\Component\Form\Exception\TransformationFailedException
Unable to reverse value for property path "startDate": Expected a string.
Caused by:
Symfony\Component\Form\Exception\TransformationFailedException
Expected a string.
So apparently my form is sending a DateTime object to my Controller, who has trouble converting it to String. In the database, the field is of type datetime.
Here is my DateTimePickerType :
class DateTimePickerType extends AbstractType
{
/**
* #return string
*/
public function getParent()
{
return 'datetime';
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
'empty_data' => new \DateTime(),
'widget' => "single_text",
'attr' => array(
'class' => 'addInput col-xs-12 dateSize noPadding noOutline dateTimePicker',
),
'format' => 'YYYY-MM-DD HH:mm',
'date_format'=>"dd/MM/yyyy hh:mm",
'html5' => false,
)
);
}
/**
* #param FormView $view
* #param FormInterface $form
* #param array $options
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars = array_replace($view->vars, array(
'empty_data' => $options['empty_data'],
'widget' => $options['widget'],
'format' => $options['date_format'],
));
}
/**
* #return string
*/
public function getName()
{
return 'date_time_picker';
}
}
I have tried many different options for the resolver. If I remove the "new \DateTime()" in "empty_data", my form now sends a null value, and I get a nice SQL error trying to insert null into the database.
Snippet of form_template.html.twig :
{% block date_time_picker_widget %}
<div class='input-group date' id='date-time-picker-{{ id }}'>
<input type="text" class="form-control" />
<span class="input-group-addon"><i class="fa fa-clock-o"></i></span>
</div>
{% include 'VMSFormTypeBundle:Template:date_time_picker_script.html.twig' %} {# link to the direct js script to make it datetimepicker #}
{% endblock %}
In date_time_picker_script.html.twig :
{% autoescape false %}
<script type="text/javascript">
$(function () {
$('#date-time-picker-{{ id }}').datetimepicker();
});
</script>
{% endautoescape %}
Snippet of my Form Builder :
$builder
->add('startDate','date_time_picker')
Thanks in advance
EDIT : Just noticed the datetime that is sent by the form ignores what I select, and instead sends the current datetime (current day, hour and minute).

Apparently this is because there's a need to convert the time format between PHP and JS. I fixed the problem by using only a DateType (time wasn't that necessary).
This might be useful for those who still need a DateTime, tho I haven't tested it : https://github.com/stephanecollot/DatetimepickerBundle

Related

Redirect back with Symfony and twig

In my Symfony project I have two views. Details view of specific entity and a button to lead me to a new view with with some other data by date param.
The problem with the code I have is of generating 'Return to previous' page button from second method back to first..
Code:
/**
* #Route("/details/{id}", name="first_method")
*/
public function firstMethod(Detail $id)
{
$workspace = $this->entityManager
->getRepository(Detail::class)
->find($id);
$build['detail'] = $detail;
$form = $this->createFormBuilder()
->add('date', DateTimeType::class, [
'data' => new \DateTime(),
'widget' => 'single_text'
])
->add(
'save',
SubmitType::class,
[
'attr' => ['class' => 'btn-submit']
]
)
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
$date = $data['date']
return $this->redirectToRoute('second_method', [
'date' => $date
]);
}
return $this->render('first-method.html.twig', [
'detail' => $detail,
]);
}
And there is that 'second' method:
/**
* #Route("/second-method/{date}", name="second_method")
*/
public function secondMethod($date)
{
return $this->render('second-method.html.twig', [
'someData' => $someData,
'date' => $date,
]);
}
I have a button on the second_method twig view which needs to return me back to method_one page.
How can accomplish that as the parameter $id is probably needed in second method but can not find a way to provide it. Maybe there is some other way? Can someone help?
I think in this first way it shoud me like:
{{ path('first_method', {'detailId':detail.id}) }}
As hinted by #msg, you should provide a second parameter to your second page. You will then need the #Entity annotation for Detail conversion and the #ParamConverter annotation for the date conversion :
/**
* #Route("/second_method/{id_detail}/{date}", name="second_method")
* #Entity("detail", expr="repository.find(id_detail)")
* #ParamConverter("date", options={"format": "!Y-m-d"})
*/
public function (Detail $detail, \DateTime $date)
// ...
The parameter conversion will perform a first sanity check and will return a 404 if the entity doesn't exist.
You will also be able to check in your controller that $detail is coherent with the date in the URL. Remember : never trust user input (this includes URLs).
And you can provide this new parameter when your redirect to your second controller :
return $this->redirectToRoute("second_method", [
"date" => $date,
"id_detail" => $workspace->getId() // You can also pass the whole object and fetch the ID in the template
]);
Then in your template :
{{ path('first_method', {'id': id_detail}) }}
Details
For ParamConverters you can find more details here : https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
I personnaly prefer the #Entity annotation for conversion of an Entity since you are sure of how the conversion is done : I find it better for when your project grows and your entities have more and more parameters. Eventhough the #ParamConverter would work in your case.
Alternative without changing the second_method route parameters, relying only on the referer.
Add the helper method to the controller:
/**
* Returns the referer url to redirect back if the request came from first_method
* Otherwise returns FALSE.
*/
private function getBackUrl(Request $request, UrlMatcherInterface $matcher)
{
$referer = $request->headers->get('referer');
if (null === $referer) {
return false;
}
$url = parse_url($referer);
if ($url['host'] != $request->getHost()) {
return false;
}
// Remove the baseurl if we're in a subdir or there's no Rewrite
$path = str_replace($request->getBaseUrl(), '', $url['path']);
try {
$route = $matcher->match($path);
} catch (ResourceNotFoundException $e) {
// Doesn't match any known local route
return false;
}
// A route has been determined, check if it matches the requirement
if ('first_method' == $route['_route']) {
return $referer;
}
return false;
}
Change the method to (The Route annotation doesn't change, just the method signature):
public function secondMethod($date, Request $request, UrlMatcherInterface $matcher)
{
return $this->render('second-method.html.twig', [
'someData' => $someData,
'date' => $date,
'back_url' => $this->getBackUrl($request, $matcher),
]);
}
In your template:
{# Show the button only if the request came from first_method (otherwise will be false) #}
{% if back_url %}
<a class="btn" href="{{ back_url }}">Return</a>
{% endif %}

Date after another date in symfony form

I have a form that contain 2 dates: start date(datedebut) and end date(datefin).
I want the end date to be always after the start date. How can i do that?
My form type:
class ReservationType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('datedebut',DateType::class,array(
'widget' => 'choice',
'years' => range(date('Y'), date('Y')+20),
))
->add('datefin',DateType::class,array(
'widget' => 'choice',
'years' => range(date('Y'), date('Y')+20),
))
->add('nbplaces')
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Bridge\TravelBundle\Entity\Reservation'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'Bridge_TravelBundle_Reservation';
}
}
You can use a Callback Validator for this. Injected into that callback is a ExecutionContextInterface by which you can access the form, and thus other form params.
Here's an example:
use Symfony\Component\Validator\Constraints;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
// …
$builder
->add('start', 'datetime',
'constraints' => [
new Constraints\NotBlank(),
new Constraints\DateTime(),
],
])
->add('stop', 'datetime', [
'constraints' => [
new Constraints\NotBlank(),
new Constraints\DateTime(),
new Constraints\Callback(function($object, ExecutionContextInterface $context) {
$start = $context->getRoot()->getData()['start'];
$stop = $object;
if (is_a($start, \DateTime::class) && is_a($stop, \DateTime::class)) {
if ($stop->format('U') - $start->format('U') < 0) {
$context
->buildViolation('Stop must be after start')
->addViolation();
}
}
}),
],
]);
Usually these kind of tasks are solved by adding validation constraints to check if value of one field is greater then the other. Implement callback validation constraint as stated in the documentation: http://symfony.com/doc/current/reference/constraints/Callback.html You can also create your custom class constraint validator and place validation logic there: http://symfony.com/doc/current/validation/custom_constraint.html
This way whenever a user tries to submit value of datefin which is less than selected value of datedebut he will see a validation error and the form will not be processed.
After that you can always add some javascript code that will filter available dates in datefin field after value in datedebut field is changed.
Also you can use dynamic form modification to render the second date field (and filter its available dates on server side) only if value of the first one is submitted. Check this out: http://symfony.com/doc/current/form/dynamic_form_modification.html

Build Form with Single Table Inheritance

I have my entity Article and one single table inheritance like this :
/**
* Article
*
* #ORM\Table(name="article")
* #ORM\Entity(repositoryClass="PM\PlatformBundle\Repository\ArticleRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="media", type="string")
* #ORM\DiscriminatorMap({"article" = "Article", "movie" = "Movie", "image" = "Image", "text" = "Text"})
*/
class Article
{
protected $id;
protected $title;
protected $description;
protected $author;
//other attributes and setters getters
}
class Image extends Article
{
private $path;
//getter setter
}
class Movie extends Article
{
private $url;
//getter setter
}
So my article's object type is either Image or movie or text only. Ok now I would like build a form wherein users can post a new article : in this form, the user has to choice between tree type (3 radios button) : image OR movie OR text only and of course the other fields : title and description. How I can do that ? Because with the command
php bin/console doctrine:generate:form myBundle:Article
The form rendered is :
class ArticleType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', TextType::class)
->add('description', TextareaType::class)
->add('save', SubmitType::class);
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'PM\PlatformBundle\Entity\Article'
));
}
}
I don't know the way to implement my STI relation in this form. Because I have not field in my Article entity/object for the type (only in my table).
I have to add a Custom ChoiceType() field but it require a attribute.
When I try to add this in the form :
->add('path', SearchType::class)
->add('url', UrlType::class)
I got this error :
Neither the property "path" nor one of the methods "getPath()", "path()", "isPath()", "hasPath()", "__get()" exist and have public access in class "PM\PlatformBundle\Entity\Article".
Because I have create an instance of Article, not an instance of Image or Movie. Initially I created a STI thinking a new instance of Article would allow me also to define the "type" of article. But not ? Right ?
You will have to make three forms (one for an Article, one for a Movie and one for an Image). Then, in your controller, you have to options to deal with them:
Either you use one action to handle the three forms (you can check wich one is submitted by using $form->isSubmitted())
You create one action by form, and set the form action URL for each form to the correct controller.
Finally, in your template, you encapsulate your forms in a div, and use the example in my previous post.
{% extends "CoreBundle::layout.html.twig" %}
{% block title %}{{ parent() }}{% endblock %}
{% block btn_scrollspy %}
{% endblock %}
{% block bundle_body %}
<div class="well">
<div class="selector">
<input type="radio" name="form-selector" value="article-form"> Article
<input type="radio" name="form-selector" value="movie-form"> Movie
<input type="radio" name="form-selector" value="image-form"> Image
</div>
<div class="form article-form" style="display: none;">
{{ form(articleForm) }}
</div>
<div class="form movie-form" style="display: none;">
{{ form(movieForm) }}
</div>
<div class="form image-form" style="display: none;">
{{ form(imageForm) }}
</div>
</div>
{% endblock %}
I agree with dragoste in the comments: you can't expect the form to deduce by himself the type of class you want to instantiate based on a value.
Roughly, Image and Movie are the same type as Article, but an Article is not an Image and/or a Movie.
You will have to check that manually. You can do that server side like explained in the comments, with a field using mapped: false to determine the type of entity you need to instantiate, or client side with javascript by using three forms (one for a movie, one for an article, one for an image) and by displaying the correct one based on your radio button.
Edit: How to display the correct form in JS?
I created a JSFiddle to show you how you can do this using jQuery : https://jsfiddle.net/61gc6v16/
With jQuery documentation, you should be able to quickly understand what this sample do, and to adapt it to your needs. :)
#Boulzy I did this and it works:
class ArticleController extends Controller
{
public function addAction(Request $request)
{
$form1 = $this->get('form.factory')->create(TextOnlyType::class);
$form2 = $this->get('form.factory')->create(ImageType::class);
$form3 = $this->get('form.factory')->create(MovieType::class);
return $this->render('PMPlatformBundle:Article:add.html.twig', array(
'ArticleTextform' => $form1->createView(),
'ArticleImageform' => $form2->createView(),
'ArticleMovieform' => $form3->createView(),
));
}
public function addArticleTextAction(Request $request)
{
$ArticleText = new TextOnly;
$form = $this->get('form.factory')->create(TextOnlyType::class, $ArticleText);
if ($request->isMethod('POST') && $form->handleRequest($request)->isValid()) {
$ArticleText->setAuthor(5);
$ArticleText->setLikes(0);
$em = $this->getDoctrine()->getManager();
$em->persist($ArticleText);
$em->flush();
$request->getSession()->getFlashBag()->add('notice', 'Annonce bien enregistrée.');
$listComments = $ArticleText->getComments();
return $this->render('PMPlatformBundle:Article:view.html.twig', array(
'article' => $ArticleText,
'listComments' => $listComments
));
}
return $this->render('PMPlatformBundle:Article:add.html.twig', array(
'form' => $form->createView(),
));
}
public function addArticleImageAction(Request $request)
{
//the same system as TextOnly
}
public function addArticleMovieAction(Request $request)
{
//the same system as TextOnly
}
}
(I override action directly in my template. With POST method.)
addAction is my contoller which is called by route for display the view of three forms. As you can see this code works as long as there is no error on submitted form. Because, in this case (when error occured when a form is submitted) each controller needs to return the initial view with the 3 forms. How I can do that ?

Add dropdown to symfony 3 form

I want to add a dropdow field for category in my form in symfony version 3, I have tried to solutions but each one have their own problem
First got all categories and pass them to my view and show them:
Action:
/**
* Creates a new News entity.
*
* #Route("/new", name="news_new")
* #Method({"GET", "POST"})
*/
public function newAction(Request $request)
{
$news = new News();
$form = $this->createForm('AppBundle\Form\NewsType', $news);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($news);
$em->flush();
return $this->redirectToRoute('news_show', array('id' => $news->getId()));
}
$em = $this->getDoctrine()->getManager();
$categories = $em->getRepository('AppBundle:Category')->findAll();
return $this->render('news/new.html.twig', array(
'news' => $news,
'form' => $form->createView(),
'categories' => $categories,
));
}
View:
{% extends 'base.html.twig' %}
{% block body %}
<h1>News creation</h1>
{{ form_start(form) }}
<label for="news_content" class="required">Category</label>
<select name="news[categoryId]">
{% for category in categories %}
<option value="{{ category.id }}">{{ category.title }}</option>
{% endfor %}
</select>
{{ form_widget(form) }}
<input class="btn btn-sm btn-success" type="submit" value="Create" />
{{ form_end(form) }}
<ul>
<li>
<a class="label label-sm label-info" href="{{ path('news_index') }}">Back to the list</a>
</li>
</ul>
{% endblock %}
The form is created as I expected but when i want to submit it, it show an validation error as bellow:
This form should not contain extra fields.
second solution that I have tried is to generate the dropdown from my Type, so in NewsType I changed the buildForm function as bellow:
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('categoryId', EntityType::class, [
'class' => 'AppBundle:Category',
'choice_label' => 'title',
])
->add('title')
->add('content')
;
}
It this way, the form also have been created nicely but after submit, an database exception returned and said:
An exception occurred while executing 'INSERT INTO news (category_id, title, content) VALUES (?, ?, ?)' with params [{}, "asdf", "asdf"]:
Catchable Fatal Error: Object of class AppBundle\Entity\Category could not be converted to string
It mean that my category_id passed as an object !
What should I do?
BTW, my english is a little weak, please do not put and minus on my post, I had been ban multiple times.
Thanks in advance.
all symfony is trying to do is to find a string representation of the Category object, so it can populate the option fields.
you can solve this in a couple of ways:
In the Category object, you can make a __toString method.
public function __toString() {
return $this->name; // or whatever field you want displayed
}
or
you can tell symfony which field to use as the label for the field. From docs
$builder->add('attending', ChoiceType::class, array(
'choices' => array(
new Status(Status::YES),
new Status(Status::NO),
new Status(Status::MAYBE),
),
'choice_label' => 'displayName', // <-- where this is getDisplayName() on the object.
));

How to display the current picture above the upload field in SonataAdminBundle?

I am using SonataAdminBundle (with Doctrine2 ORM) and I have successfully added a file upload feature to my Picture model.
I would like, on the Show and Edit pages, to display a simple <img src="{{ picture.url }} alt="{{ picture.title }} /> tag just above the relevant form field (provided that the Picture being edited is not new, of course), so that the user may see the current photo, and decide whether to change it or not.
After hours of research, I've been unable to figure out how to do it. I suppose I need to override some template, but I'm a bit lost...
Can somebody give me a hint?
Thank you!
Here is the relevant section of my PictureAdmin class.
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('category', NULL, ['label' => 'Catégorie'])
->add('title', NULL, ['label' => 'Titre'])
->add('file', 'file', ['required' => false, 'label' => 'Fichier']) // Add picture near this field
->add('creation_date', NULL, ['label' => 'Date d\'ajout'])
->add('visible', NULL, ['required' => false, 'label' => 'Visible'])
->add('position', NULL, ['label' => 'Position']);
}
protected function configureShowFields(ShowMapper $showMapper)
{
$showMapper
->add('id', NULL, ['label' => 'ID'])
->add('category', NULL, ['label' => 'Catégorie'])
->add('title', NULL, ['label' => 'Titre'])
->add('slug', NULL, ['label' => 'Titre (URL)'])
->add('creation_date', NULL, ['label' => 'Date d\'ajout'])
->add('visible', NULL, ['label' => 'Visible'])
->add('position', NULL, ['label' => 'Position']);
// Add picture somewhere
}
I have managed to put the image above the field in the edit form. But my solution is a little bit specific, because I use Vich Uploader Bundle to handle uploads, so the generation of the image url was a little bit easier thanks to bundle helpers.
Let's look at my example, a film poster field in a film entity.
This is part of my admin class:
//MyCompany/MyBundle/Admin/FilmAdmin.php
class FilmAdmin extends Admin {
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('title')
....
->add('poster', 'mybundle_admin_image', array(
'required' => false,
))
}
mybundle_admin_image is handled by a custom field type, that is just a child of file type by setting it's getParent method: (don't forget to register your type class as a service)
//MyCompany/MyBundle/Form/Type/MyBundleAdminImageType.php
public function getParent()
{
return 'file';
}
Then I have a template that extends Sonata's default styling, and I have it included in the admin class:
//MyCompany/MyBundle/Admin/FilmAdmin.php
public function getFormTheme() {
return array('MyCompanyMyBundle:Form:mycompany_admin_fields.html.twig');
}
And finally I have a block for my custom image type that extends the basic file type:
//MyCompany/MyBundle/Resources/views/Form/mycompany_admin_fields.html.twig
{% block mybundle_admin_image_widget %}
{% spaceless %}
{% set subject = form.parent.vars.value %}
{% if subject.id and attribute(subject, name) %}
<a href="{{ asset(vich_uploader_asset(subject, name)) }}" target="_blank">
<img src="{{ asset(vich_uploader_asset(subject, name)) }}" width="200" />
</a><br/>
{% endif %}
{% set type = type|default('file') %}
<input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
{% endspaceless %}
{% endblock %}
This causes that a 200px wide preview of image (if exists) is shown above the upload field, linked to it's full size version opening in new tab. You can customize it as you want, e.g. adding a lightbox plugin.
you can easily do this on edit page by helpers(FormMapper->setHelps) or option "help" pass on FormMapper
protected function configureFormFields(FormMapper $formMapper) {
$options = array('required' => false);
if (($subject = $this->getSubject()) && $subject->getPhoto()) {
$path = $subject->getPhotoWebPath();
$options['help'] = '<img src="' . $path . '" />';
}
$formMapper
->add('title')
->add('description')
->add('createdAt', null, array('data' => new \DateTime()))
->add('photoFile', 'file', $options)
;
}
You can easily do this on show page
by template attribute pass on $showmapper
->add('picture', NULL, array(
'template' => 'MyProjectBundle:Project:mytemplate.html.twig'
);
and inside your template you get current object so u can call get method and pull image path
<th>{% block name %}{{ admin.trans(field_description.label) }}{% endblock %}</th>
<td>
<img src="{{ object.getFile }}" title="{{ object.getTitle }}" />
</br>
{% block field %}{{ value|nl2br }}{% endblock %}
</td>
To show image in edit mode you have to override fileType or you have to create your own customType on top of fileType
There is also some bundle which is having this kind of functionality
check out this GenemuFormBundle
Solution for Symfony3
The answer from #kkochanski is the cleanest way I found so far. Here a version ported to Symfony3. I also fixed some bugs.
Create a new template image.html.twig for your new form type (full path: src/AppBundle/Resources/views/Form/image.html.twig):
{% block image_widget %}
{% spaceless %}
{% set type = type|default('file') %}
<input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
{% if image_web_path is not empty %}
<img src="{{ image_web_path }}" alt="image_photo"/>
{% endif %}
{% endspaceless %}
{% endblock %}
Register the new form type template in your config.yml:
twig:
form_themes:
- AppBundle::Form/image.html.twig
Create a new form type and save it as ImageType.php (full path: src/AppBundle/Form/Type/ImageType.php):
<?php
namespace AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormBuilderInterface;
/**
* Class ImageType
*
* #package AppBundle\Form\Type
*/
class ImageType extends AbstractType
{
/**
* #return string
*/
public function getParent()
{
return 'file';
}
/**
* #return string
*/
public function getName()
{
return 'image';
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'image_web_path' => ''
));
}
/**
* #param FormView $view
* #param FormInterface $form
* #param array $options
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['image_web_path'] = $options['image_web_path'];
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->setAttribute('image_web_path', $options['image_web_path'])
;
}
}
If you have done this. You can just import the new ImageType in your entity admin class:
use AppBundle\Form\Type\ImageType
And then, finally use the new form type without any inline-html or boilerplate code in configureFormFields:
$formMapper
->add('imageFile', ImageType::class, ['image_web_path' => $image->getImagePath()])
;
Instead of $image->getImagePath() you have to call your own method that returns the url to your image.
Screenshots
Creating a new image entity using sonata admin:
Editing a image entity using sonata admin:
You can simple do by this way
$image = $this->getSubject();
$imageSmall = '';
if($image){
$container = $this->getConfigurationPool()->getContainer();
$media = $container->get('sonata.media.twig.extension');
$format = 'small';
if($webPath = $image->getImageSmall()){
$imageSmall = '<img src="'.$media->path($image->getImageSmall(), $format).'" class="admin-preview" />';
}
}
$formMapper->add('imageSmall', 'sonata_media_type', array(
'provider' => 'sonata.media.provider.image',
'context' => 'default',
'help' => $imageSmall
));
Teo.sk wrote the method of showing images using VichUploader. I found an option which allow you to show images without this bundle.
First we need to create our form_type. There is tutorial: symfony_tutorial
In main Admin class:
namespace Your\Bundle;
//.....//
class ApplicationsAdmin extends Admin {
//...//
public function getFormTheme() {
return array_merge(
parent::getFormTheme(),
array('YourBundle:Form:image_type.html.twig') //your path to form_type template
);
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper->add('file_photo', 'image', array(
'data_class' => 'Symfony\Component\HttpFoundation\File\File',
'label' => 'Photo',
'image_web_path' => $this->getRequest()->getBasePath().'/'.$subject->getWebPathPhoto()// it's a my name of common getWebPath method
))
//....//
;
}
}
Next part is a code from ImageType class.
namespace Your\Bundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class ImageType extends AbstractType
{
public function getParent()
{
return 'file';
}
public function getName()
{
return 'image';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'image_web_path' => ''
));
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['image_web_path'] = $options['image_web_path'];
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->setAttribute('image_web_path', $options['image_web_path'])
;
}
}
And on the end time for image_type twig template.
{% block image_widget %}
{% spaceless %}
{% set type = type|default('file') %}
<input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
<img src="{{ image_web_path }}" alt="image_photo"/>
{% endspaceless %}
{% endblock %}
For me it's working! I'm also using avalanche bundle to resize images.
There is an easy way - but you will see the picture below the upload button.
SonataAdmin lets put raw HTML into the ‘help’ option for any given form field. You can use this functionality to embed an image tag:
protected function configureFormFields(FormMapper $formMapper) {
$object = $this->getSubject();
$container = $this->getConfigurationPool()->getContainer();
$fullPath = $container->get('request')->getBasePath().'/'.$object->getWebPath();
$formMapper->add('file', 'file', array('help' => is_file($object->getAbsolutePath() . $object->getPlanPath()) ? '<img src="' . $fullPath . $object->getPlanPath() . '" class="admin-preview" />' : 'Picture is not avialable')
}

Categories