I have installed and configured SonataPageBundle as explained in the documentation. i'm trying to figure out how to render in SonataAdmin the content of my custom block. I declared my block in config.yml, i created a service,controller, routing and the page that i want to edit the block of content. I have already run the following command:
php app/console sonata:page:update-core-routes --site=all
php app/console sonata:page:create-snapshots --site=all
Unfortunately the content inside of my block is not render in the backend. i have two pictures that show you my problem:
My backend:
This is how i would like it to be of course with my content in my block.
Sonata Sandbox:
This is what i did so far:
Routing:
block:
path: index/block
defaults: { _controller: FLYBookingsBundle:Default:myblock }
DefaultController:
public function myblockAction()
{
return $this->render('FLYBookingsBundle:Default:myblock.html.twig');
}
Service.yml
sonata.block.service.myblock:
class: FLY\BookingsBundle\Block\MyBlockService
arguments: [ "sonata.block.service.myblock", #templating, #doctrine.orm.entity_manager ]
tags:
- { name: sonata.block }
MyBlockService
<?php
namespace FLY\BookingsBundle\Block;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\CoreBundle\Validator\ErrorElement;
use Sonata\BlockBundle\Model\BlockInterface;
use Sonata\BlockBundle\Block\BaseBlockService;
use Sonata\BlockBundle\Block\BlockContextInterface;
class MyBlockService extends BaseBlockService
{
protected $em;
public function __construct($type, $templating, $em)
{
$this->type = $type;
$this->templating = $templating;
$this->em = $em;
}
public function getName()
{
return 'MyBlock';
}
public function getDefaultSettings()
{
return array();
}
public function validateBlock(ErrorElement $errorElement, BlockInterface $block)
{
}
public function buildEditForm(FormMapper $formMapper, BlockInterface $block)
{
}
public function execute(BlockContextInterface $blockContext, Response $response = null)
{
$settings = array_merge($this->getDefaultSettings(), $blockContext->getBlock()->getSettings());
$data = count($this->em->getRepository("ApplicationSonataPageBundle:Page")->findAll());
return $this->renderResponse('FLYBookingsBundle:Default:myblock.html.twig', array(
'block' => $blockContext->getBlock(),
'settings' => $settings,
'data' => $data,
), $response);
}
}
myblock.html.twig
{% extends 'SonataPageBundle:Block:block_base.html.twig' %}
{% block block %}
<p> test test test </p>
{% endblock %}
.
sonata_block:
default_contexts: [sonata_page_bundle]
context_manager: sonata.page.block.context_manager
blocks:
sonata.user.block.menu:
sonata.admin.block.admin_list:
contexts: [admin]
sonata.block.service.myblock: ~
sonata.block.service.container:
sonata.page.block.container:
etc....
Related
I am writing a Twig function in Symfony 4 but I cannot get it to work...
The extension class
<?php
namespace App\Twig;
use App\Utils\XXX;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class XXXExtension extends AbstractExtension
{
/**
* #return array|TwigFunction|TwigFunction[]
*/
public function getFunctions()
{
return new TwigFunction('showControllerName', [$this, 'showControllerName']);
}
public function showControllerName($sControllerPath)
{
return XXX::getControllerName($sControllerPath);
}
}
I have autowire set to true in services.yaml but just in case i tried with this also:
App\Twig\XXXExtension:
public: true
tags:
- { name: twig.extension }
usage in html.twig
{% set controllerName = showControllerName(app.request.get('_controller')) %}
and the response i get after this is:
HTTP 500 Internal Server Error
Unknown "showControllerName" function.
You need to return an array of functions, you are only returning one.
...
public function getFunctions()
{
return [
new TwigFunction('showControllerName', [$this, 'showControllerName']),
];
}
...
I'm trying to get the current user in my NotificationExtension.php. But the page become very slow to load and I also get this error:
Error: Call to a member function getUser() on null
The error say that is impossible to get the current user, but i'm login.
This is my service:
notification:
class: Application\Sonata\UserBundle\Twig\NotificationExtension
arguments: ['#doctrine.orm.entity_manager', '#service_container', '#security.context']
tags:
- { name: twig.extension }
NotificationExtension :
<?php
namespace Application\Sonata\UserBundle\Twig;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Twig_Extension;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Security\Core\Security;
class NotificationExtension extends \Twig_Extension
{
protected $container;
protected $em;
public function __construct(EntityManager $em,ContainerInterface $container, SecurityContext $context)
{
$this->container = $container;
$this->em = $em;
$this->doctrine = $container->get('doctrine');
$this->context = $context;
}
public function getGlobals()
{
$user = $this->container->get('security.context')->getToken()->getUser();
return(array(
'unreadMessagesCount' => $this->em->getRepository('ApplicationSonataUserBundle:Notif')->findBy(
array(
'user' => $user,
'seen' => true
),
array('date' => 'DESC')
)));
}
public function getName()
{
return 'notification';
}
}
ADD:
service:
notification:
class: Application\Sonata\UserBundle\Twig\NotificationExtension
arguments: ['#doctrine.orm.entity_manager','#security.token_storage']
tags:
- { name: twig.extension }
get current user:
public function getUser()
{
return $this->tokenStorage->getToken()->getUser();
}
Define instead a service as a global Twig variable:
# app/config/config.yml
twig:
# ...
globals:
user_notification: '#app.user_notification'
The service class:
// src/AppBundle/Twig/Globals/UserNotification.php
class UserNotification
{
private $tokenStorage;
// ...
public function __construct(TokenStorageInterface $tokenStorage, ...)
{
$this->tokenStorage = $tokenStorage;
// ...
}
public function getUnreadMessages()
{
if (null === $token = $this->tokenStorage->getToken()) {
return array();
}
$user = $token->getUser();
// $unreadMessages = <DB query for get the unread messages from current user>
return $unreadMessages;
}
}
The service definition:
# app/config/config.yml
services:
app.user_notification:
class: AppBundle\Twig\Globals\UserNotification
arguments: ['#security.token_storage', ...]
Finally, for all templates you can to use this service:
# foo.html.twig
{{ user_notification.unreadMessages|length }}
Whenever the global variable is accessed in the template, the service will be requested from the service container and you get access to that object.
More information http://symfony.com/doc/current/templating/global_variables.html
you can access the username like that : app.user.username
If you want to check if the user is logged, you can use the is_granted twig function.
eg :
{% if is_granted("ROLE") %}
Hi {{ app.user.username }}
{% endif %}
I want to apply a filter by default on all my variables, to avoid this :
{% set var = "%gg% man" %}
{% block body %}
<h1>You are a {{ var|replace({'%gg%':good, '%bb%':bad}) }}</h1>
{% block body %}
on output :
You are a good man
is there a solution ?
You can create a filter twig.
// src/AppBundle/Twig/AppExtension.php
namespace AppBundle\Twig;
class AppExtension extends \Twig_Extension
{
public function getFilters()
{
return array(
new \Twig_SimpleFilter('customReplace', array($this, 'customReplaceFilter')),
);
}
public function customReplaceFilter(yourArguments)
{
//do your replace here in PHP
return [your string after replace];
}
public function getName()
{
return 'app_extension';
}
}
And in your twig
{{ var|customReplace(yourArguments) }}
And don't forget this in your app/config/services.yml :
services:
app.twig_extension:
class: AppBundle\Twig\AppExtension
public: false
tags:
- { name: twig.extension }
I'm trying to create my own Twig filter. I followed this tuto Symfony Official Book.
But I get this error The filter "avatar" does not exist in src/Acme/Bundle/StoryBundle/Resources/views/Story/storyList.html.twig
Here is my AvatarExtension.php
<?php
namespace AppBundle\Twig;
class AvatarExtension extends \Twig_Extension
{
public function getFilters()
{
return array(
new \Twig_SimpleFilter('avatar', array($this, 'avatar')),
);
}
public function getName()
{
return 'avatar_extension';
}
public function avatar($user)
{
if ($user->getPicture() && $user->getPicture() != '') {
return $user->getPicture();
} else {
return '/images/default-avatar.jpg';
}
}
}
And my AppBundle/Resources/config/services.yml
services:
app.twig.avatar_extension:
class: AppBundle\Twig\AvatarExtension
tags:
– { name: twig.extension }
The template using the filter is not in the same bundle as the Twig extension, but since it is a service, it shouldn't be a problem.
Here is how I call it : {{ story.author|avatar }}
Do you have any idea what the problem might be ?
EDIT
# Twig Configuration
twig:
debug: "%kernel.debug%"
strict_variables: "%kernel.debug%"
globals:
uploadTmpDir: %upload.tmp.relative.dir%
Ok I found the solution. Here is the services.yml
app.twig.avatar_extension:
class: AppBundle\Twig\AvatarExtension
tags:
- { name: twig.extension }
And here is the ExtensionClass:
<?php
namespace AppBundle\Twig;
class AvatarExtension extends \Twig_Extension
{
public function getFilters()
{
return array(
new \Twig_SimpleFilter('avatar', array($this, 'avatarFilter')),
);
}
public function avatarFilter($user)
{
if ($user->getPicture() && $user->getPicture() != '') {
return $user->getPicture();
} else {
return '/images/default-avatar.jpg';
}
}
public function getName()
{
return 'avatar_extension';
}
}
I guess the function name must have the Filter suffix
Problem
I used this solution https://stackoverflow.com/a/15167450/2910183, but I have a following error when I'm trying open admin dashboard (http://localhost/app_dev.php/admin/dashboard). This happens also after cleaning cache.
ClassNotFoundException: Attempted to load class "AcmeBlockService" from namespace "Acme\ProductBundle\Block" in ~/htdocs/symfony2training/app/cache/dev/appDevDebugProjectContainer.php line 2216. Do you need to "use" it from another namespace?
Does anybody know where is problem?
Code
Here is part of my app/config/config.yml
sonata_block:
default_contexts: [cms]
blocks:
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)
acme.block.products:
sonata_admin:
dashboard:
blocks:
# display a dashboard block
- { position: left, type: acme.block.products }
- { position: left, type: sonata.admin.block.admin_list }
Part of my src/Acme/ProductBundle/Resources/config/services.yml
services:
acme.block.products:
id: acme.block.products
class: Acme\ProductBundle\Block\ProductsBlockService
arguments:
- { name: service, id: templating }
tags:
- { name: sonata.block }
My src/Acme/ProductBundle/Document/Products.php file in :
<?php
namespace Acme\ProductBundle\Block;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Sonata\BlockBundle\Model\BlockInterface;
use Sonata\BlockBundle\Block\BlockContextInterface;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Validator\ErrorElement;
class ProductsBlockService extends BaseBlockService {
public function getName() {
return 'Products';
}
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) {
$settings = array_merge($this->getDefaultSettings(), $block->getSettings());
return $this->renderResponse('AcmeProductBundle:Block:admin_products.html.twig', array(
'block' => $block,
'settings' => $settings
), $response);
}
}
Solution
I looked into another block (vendor/sonata-project/user-bundle/Sonata/UserBundle/Block/AccountBlockService.php) and I realized, that its file name is like class name. So I changed src/Acme/ProductBundle/Document/Products.php to src/Acme/ProductBundle/Document/ProductsBlockService.php
It works, but another error appeared:
FatalErrorException: Compile Error: Declaration of Acme\ProductBundle\Block\ProductsBlockService::execute() must be compatible with Sonata\BlockBundle\Block\BlockServiceInterface::execute(Sonata\BlockBundle\Block\BlockContextInterface $blockContext, Symfony\Component\HttpFoundation\Response $response = NULL) in ~/htdocs/symfony2training/src/Acme/ProductBundle/Block/ProductsBlockService.php line 0
The solution is written in the error message: Acme\ProductBundle\Block\ProductsBlockService::execute() must be compatible with Sonata\BlockBundle\Block\BlockServiceInterface::execute(). So I looked into my helper (AccountBlockService.php from 1.), compare execute() methods and write my own solution.
So in the end my src/Acme/ProductBundle/Document/ProductsBlockService.php file looks as below.
<?php
namespace Acme\ProductBundle\Block;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Validator\ErrorElement;
use Sonata\BlockBundle\Block\BaseBlockService;
use Sonata\BlockBundle\Block\BlockContextInterface;
use Sonata\BlockBundle\Model\BlockInterface;
use Sonata\UserBundle\Menu\ProfileMenuBuilder;
use Sonata\UserBundle\Model\UserInterface;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;
class ProductsBlockService extends BaseBlockService {
public function getName() {
return 'Products';
}
public function validateBlock(ErrorElement $errorElement, BlockInterface $block) {
}
public function buildEditForm(FormMapper $formMapper, BlockInterface $block) {
}
public function setDefaultSettings(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'template' => 'AcmeProductBundle:Block:admin_products.html.twig',
'ttl' => 0
));
}
public function execute(BlockContextInterface $blockContext, Response $response = null) {
return $this->renderPrivateResponse($blockContext->getTemplate(), array(
'block' => $blockContext->getBlock(),
'context' => $blockContext,
), $response);
}
}