Controller filenames starting with P cause Routing problems - symfony

I recently created a controller called PublishBannerController.php It was a cut and paste of a previous Controller with all the various parts adjusted accordingly. The only problem is, I get this error when I try and load it:
FatalErrorException: Error: Class 'Me\MyBundle\Controller?ublishBannerController' not found in /.../public_html/app/cache/dev/appDevDebugProjectContainer.php line 3190
I have replaced the square that appears, with a ? as the square really messes with stackOverflows editor.
It seems that the Symfony 2 parser converts paths containing \P into the special character that that represents.
As you can see below in appDevDebugProjectContainer.php anywhere where it is \P it converts it, even in the comments. Anywhere it contains _P etc. it is fine.
/*
* Gets the 'me.controller.publishbanner' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* #return Me\MyBundle\Controller?ublishBannerController A Me\MyBundle\Controller?ublishBannerController instance.
*/
protected function getMe_Controller_PublishbannerService()
{
$this->services['me.controller.publishbanner'] = $instance = new \Me\MyBundle\Controller?ublishBannerController();
$instance->setContainer($this);
return $instance;
}
Here is my routing.yml
me_site_publishbanner:
pattern: /publishbanner
defaults: { _controller: me.controller.publishbanner:indexAction }
And services.yml
me.controller.publishbanner:
class: "Me\MyBundle\Controller\PublishBannerController"
shared: true
calls:
- [ setContainer, ["#service_container"] ]
Am I making a mistake somewhere or is this a bug in Symfony 2? Are you basically not allowed to create controller names starting with P?
Obviously, it is a Unicode issue and relates to this:
http://php.net/manual/en/regexp.reference.unicode.php
And I have this set in my php.ini:
default_charset = "UTF-8"
I just wasn't expecting to come across this sort of issue in Symfony 2.

Do not use double quotes in your yaml file for string delimiters. It would appear that, with double quotes, \ characters get converted in to their escaped values.
No real need to use any quotes at all for class names.
If you do want to quote things like #service to avoid warnings that some broken yaml processor issue then use single quotes.

Related

How to render a view from service class in symfony?

I'm trying to make a function in my service class, that render a twig page. I've tried to do like this:
service.yml:
********
parameters:
error.class: AppBundle\Utils\Error
services:
app.error:
class: '%error.class%'
arguments: [#templating]
Error.php (service class):
****
class Error
{
public function __construct($templating)
{
$this->templating = $templating;
}
public function redirectToError($condition,$message)
{
if($condition){
return $this->templating->render('default/error.html.twig',array(
'error_message' => $message,
));
}
}
}
and error.html.twig that have some random text to see if it gets there.
After that I get this answer from browser:
Can somebody to tell me what is the problem?
YAML can be a bit iffy when it comes to syntax, make sure your using all spaces (no tab chars). And makes sure every indentation is the same amount of space characters. Like 2/4/6/8 for each level or 4/8/12 etc if you prefer 4 wide.
The code you posted should be fine, but its probably something silly as described above. If it was actually a wrong section/ parameter in the file symfony should tell you what is unexpected as it actually validates YAML files on its content.
Allright so ['#templating'] takes care of the YAML parse error, the next part is how to use a service. Which is done using the service container.
In a controller there is an alias for it and you can do something like:
// required at the top to use the response class we use to return
use Symfony\Component\HttpFoundation\Response;
// in the action we use the service container alias
// short for $this->container->get('app.error');
$content = $this->get('app.error')->redirectToError(true, 'Hello world');
// as your redirectToError function returns a templating->render, which only returns a
// string containing the the rendered template, however symfony
// requires a Response class as its return argument.
// so we create a response object and add the content to it using its constructor
return new Response($content);
A few small things:
$condition, is probably likely to change if not it seems it should not be in the function but around the function call, as it seems weird to call an redirectToError but there is no error, instead we just call it when we do have an error.
And its recommended to if you are setting a class variable to define it (details on visibility):
class Error {
// visibility public, private, protected
protected $templating;
You should put ' around #templating
services:
app.error:
class: AppBundle\Utils\Error
arguments: ['#templating']

getting the gettext behavior using the Po File Loader in the translation provider in Silex

I'm using the translation provider and the PoFileLoader in Silex and everything works super great.
$app['translator'] = $app->share($app->extend('translator', function($translator, $app) {
$translator->addLoader('po', new PoFileLoader());
$translator->addResource('po', __DIR__.'/resources/translations/de.po', 'de');
}
The only problem that I have is how it treats strings that doesn't have translation yet. I want them to be ignored and use the source instead of an empty string. Like the way gettext function treats the po files.
Is there any option for that or should I override the PoFileLoader class?
The only problem that I have is how it treats strings that doesn't have translation yet. I want them to be ignored and use the source instead of an empty string. Like the way gettext function treats the po files.
This is something the translator does, not the file loader. If there is no translation found in the message catalogue, the translator will just replace the parameters and return the source.
See also the source:
public function get($id, $domain = 'messages')
{
// ... all the loading logic
// if everything failed, just return the source
return $id;
}
This method is called in the Translator#trans method.

Display nested list with doctrine2 and zf2

Following this tutorial and putting in all together to make it work in my project, just to display a nested list (using doctrine 2 and zf2) , I can not enter into the foreach. Using this snippet of code:
$root_categories = $em->getRepository('Controleitor\Model\Entity\Category')->findBy(array('parent_category' => null));
$collection = new \Doctrine\Common\Collections\ArrayCollection($root_categories);
$category_iterator = new \MYMODULE\Model\Entity\RecursiveCategoryIterator($collection);
$recursive_iterator = new \RecursiveIteratorIterator( $category_iterator, \RecursiveIteratorIterator::SELF_FIRST);
foreach ($recursive_iterator as $index => $child_category){
echo 'test';
}
Debug::dump($recursive_iterator);die;
I'm expecting to print the 'test' string but it only print this:
object(RecursiveIteratorIterator)#414 (0) {}
But when I do before the dump:
$recursive_iterator->current()->getTitle();
I got the title.. It fails somehow looping the \Doctrine\Common\Collections\ArrayCollection object.
If you're using different Debug class instead of Doctrine's one, that may the suspect. Try Doctrine\Common\Util\Debug::dump().
Explain comes from official documentation:
Lazy load proxies always contain an instance of Doctrine’s
EntityManager and all its dependencies. Therefore a var_dump() will
possibly dump a very large recursive structure which is impossible to
render and read. You have to use Doctrine\Common\Util\Debug::dump() to
restrict the dumping to a human readable level. Additionally you
should be aware that dumping the EntityManager to a Browser may take
several minutes, and the Debug::dump() method just ignores any
occurrences of it in Proxy instances.
I had the same issue. I've discussed with the author of this tutorial, he recommended me to check the valid() function of the RecursiveCategoryIterator class and there was the problem.
Since I was using "use" statetment and left a backslash before th class name:
use Entity\Category;
use Doctrine\Common\Collections\Collection;
class RecursiveCategoryIterator implements \RecursiveIterator
{
//.......
public function valid()
{
return $this->posts->current() instanceof \Category;
}
There ware two ways to solve this problem:
1. To remove the backslash:
return $this->posts->current() instanceof Category;
2. To use full namespace:
use Entity\Category; // remove this line
//.......
return $this->posts->current() instanceof \Entity\Category;
Hope that helps.

Symfony2 Sluggable multiple separators

Using the Gedmo Sluggable behavior in Symfony2, I need to know if there is a way to allow both slashes (/) and dashes (-) as word separators. In other words, one specific separator as usual, but leaving alone the other specific special character so it is ignored. I want to do something like this as a slug:
products/some-product
This allows me to use slugs in the URL that are categorized via the slash and separate the spaces via the dash. But, for instance, if the separator is "/", the "-" will be replaced as well instead of left alone.
I've looked through the related Sluggable class code (Urlizer) and see a lot of regex going on, but I'm not sure where I should override to allow slashed and/or dashes to NOT be substituted along with everything else.
Turns out you can accomplish this by creating your own class that extends Urlizer, then setting it as the callable for the listener instead of Gedmo's class.
When using STOF doctrine extensions, a sluggable listener is created as a service, but it is set as private so you can't normally access. So you must first create an alias to this listener in your own config:
services:
sluggable.listener:
alias: stof_doctrine_extensions.listener.sluggable
You must then create your class. There is a setter called setTransliterator() that you can use to call your own transliterator, which you can then use to inject what you need to modify the slugging process. The function postProccessText() is what you really want to modify, the transliterate() function is just what is callable:
namespace My\Bundle\Util;
use Gedmo\Sluggable\Util\Urlizer as BaseUrlizer;
class Urlizer extends BaseUrlizer
{
public static function transliterate($text, $separator = '-')
{
// copy the code from the parent here
}
private static function postProcessText($text, $separator)
{
// copy code from parent, but modify the following part:
$text = strtolower(preg_replace('/[^A-Z^a-z^0-9^\/]+/', $separator,
preg_replace('/([a-z\d])([A-Z])/', '\1_\2',
preg_replace('/([A-Z]+)([A-Z][a-z])/', '\1_\2',
preg_replace('/::/', '/', $text)))));
}
}
The regular expressions of postProcessText() are what you want to modify to your liking. After that, you must make your function the callable right before you persist, and you're good to go:
// custom transliterator
$listener = $this->get('sluggable.listener');
$listener->setTransliterator(array('My\Bundle\Util\Urlizer', 'transliterate'));
$em->flush();

Best way for extending a entity in doctrine

I have a User Entity in a small Framework made by me. Now i want to use this User Entity in several projects.
But in some projects I want to add a few fields to the User Entity without modifying the file.
What I tried so far:
I created a new DefaultUser Entity in a DefaultUser Bundle and made the User Entity a mappedsuperclass. But now I can't make a association in other entities like
/*
* #ORM\ManyToOne(targetEntity="User", inversedBy="jobs")
* #ORM\JoinColumn(name="user", referencedColumnName="id")
*/
private $user;
Because Doctrine can't find the id column in the user entity. This only works if I specify the DefaultUser Entity. According to the doctrine documentation this only works on many to many associations if only one leaf exists.
Then I tried Single Table Inheritance. This works fine but I have to modify the DiscriminatorMap if I want to extend my user entity which is shared acros multiple projects...
So whats the best way to extend the UserEntity?
I have precisely the same problem - I have just switched from RedBean to Doctrine (for a project using the Zend Framework), and the structure of my classes did not take into account this issue. The core problem is that maps in Doctrine have a one to one relationship with classes, as far as I can work out. What we are looking for is a way to have one concrete class (the UserEntity) that uses a map from an abstract class (the DefaultUser). My solution, which may be something of a hack (I've only been using Doctrine for a couple of days), works for YAML at least:
Create a new mapping driver extending the YAML driver, and override the _loadMappingFile method with something like this:
class MyLibrary_Doctrine_Mapping_Driver_YamlExtended extends MyLibrary_Doctrine_Mapping_Driver_YamlExtended
{
protected $_basicEntityFolder;
protected function _loadMappingFile($file)
{
$entMaps = parent::_loadMappingFile($file);
//merge this with any extensions if defined
foreach($entMaps as $ent => $map)
{ //load the relevant map
if (!isset($map['extendEntity'])) {
continue;
}
$fileName = $this->_basicEntityFolder . DIRECTORY_SEPARATOR . str_replace('\\', '.', $map['extendEntity']) . $this->_fileExtension;
$extendedMaps = $this->_loadMappingFile($fileName);
if (!is_array($extendedMaps[$map['extendEntity']])) {
throw new MyProject_Doctrine_Exception("Entity to extend from could not be found.");
}
//merge so that the file lower in the class hierachy always overrides the higher
$map = array_merge($extendedMaps[$map['extendEntity']], $map);
//clear the extendEntity value
unset($map['extendEntity']);
$entMaps[$ent] = $map;
}
return $entMaps;
}
public function setExtendedEntitiesFolder($path)
{
$this->_basicEntityFolder = $path;
}
}
I then have two yaml files, in different folders, like this:
#MyApplication/Entities/Maps/Entities.User.dcm.yml
Entities\User:
extendEntity: LibraryEntities\User
That is the file in the application. Then in the library I have
#Library/Entities/Maps/ExtendedEntities/LibraryEntities.User.dcm.yml
LibraryEntities\User:
type: entity
table: user
fields:
username:
type: text
nullable: true
password:
type: text
nullable: true
defaultProfile:
type: text
nullable: true
column: default_profile
The reason it is in an ExtendedEntities folder is so I can define mappedSuperclasses in the library using a normal namespace, and Doctrine will load those automatically when a class extends them, but these extendedentities are outside of the path for Doctrine's usual class inheritance loading (if they were all in the normal folder structure then for eg "class ApplicationUser extends LibraryUser" Doctrine would try to load the config for LibraryUser because it would find it, and then cause the same error you have already encountered).
Then when I set up my $em I provide it with my driver:
$driverImpl = new MyLibrary_Doctrine_Mapping_Driver_YamlExtended(array(APPLICATION_PATH . '/entities/maps',
LIBRARY_PATH . '/Entities/Maps'));
$driverImpl->setExtendedEntitiesFolder(LIBRARY_PATH . '/Entities/Maps/ExtendedEntities');
Notice that this solution allows inheritance chains defined by 'extendEntity' (because the _loadMappingFile method is recursive). Also that any configuration file lower down the chain can overwrite any property already defined, so even if in your library yaml you had:
username:
type: text
Say you had a project in which usernames where integers you can simply override it in your application config with
username:
type: int
or whatever.
And therefore this solves the problem of defining Doctrine style inheritance on the base class. In every project you can define the DiscriminatorMap however you like.
In principle the same solution could be applied to annotations, though extending the annotation driver is a little more complicated, because it doesn't simply read metadata by reading one file in one go and converting it to an array, but makes numerous requests to the annotation reader, which means implementing this structure would be trickier.
I'd be very interested to know how other people have solved this problem.

Resources