Display nested list with doctrine2 and zf2 - recursion

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.

Related

dynamic class loading - Attempted to load class _CLASS_ from namespace _NAMESPACE_

So i try to load a class inside a service in Symfony4.
It doesn't matter if i load it as classname or as App\to\class\name\classname.
It generates the same error.
Other posts said you need to add the whole fully qualified classname..
This doesn't work. Am I missing something?
Code below:
<?php
// src/Service/AdwordsService.php
namespace App\Service;
use App\Service\AdTypes\ExpendedTextAdTypes as ExpendedTextAdTypes;
class AdwordsService{
...
public function getAdsModel($ad) //<-- for example "EXPANDED_TEXT_AD"
{
$type = explode('_',strtolower($ad->getType()));
$modelName = array_map('ucfirst', $type);
$modelName = implode('',$modelName).'Types';
// will load ExpandedTextAdTypes
return new $modelName($ad);
}
...
}
Class that it tries to load:
<?php
// src/Service/AdTypes/ExpendedTextAdTypes.php
namespace App\Service\AdTypes;
class ExpendedTextAdTypes
{
private $adData;
public function __construct($ad)
{
$this->adData = $ad;
}
}
The ultimate problem(s) was a simple typo: EXPANDED_TEXT_AD vs EXPENDED_TEXT_AD
along with the need to use a fully qualified class name:
// No need for any use statements
// use App\Service\AdTypes\ExpendedTextAdTypes as ExpendedTextAdTypes;
public function getAdsModel($ad) //<-- for example "EXPENDED_TEXT_AD"
{
$type = explode('_',strtolower($ad));
$modelName = array_map('ucfirst', $type);
$modelName = implode('',$modelName).'Types';
$modelName = 'App\\Service\\AdTypes\\' . $modelName; // Add this
return new $modelName($ad);
}
As a rule, typo questions are considered to be off-topic. But this question actually has two issues as well as pointing out that the use statement is not needed. So I guess it can qualify as an answer.
The question's title is also misleading. I clicked on the question because I had never seen CLASS in an error message. It would have been better to have posted the actual error message thus possible making it easier to detect the typo.
And finally, a bit of unsolicited advice. This sort of transformation from EXPENDED_TEXT_AD to ExpendedTextAdTypes can be difficult to maintain and definitely locks you in to a class naming scheme. Why not just use ExpendedTextAd instead of EXPENDED_TEXT_AD? Symfony does this sort of thing all the time.

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']

Using reflection in PHPUnit

I'm testing a private method of a class used in Symfony2 project with PHPUnit.
I'm using the private methods testing strategy (through reflection) described by many developers such as http://aaronsaray.com/blog/2011/08/16/testing-protected-and-private-attributes-and-methods-using-phpunit/
But unfortunately, I got the following error:
There was 1 error: 1) My\CalendarBundle\Tests\Calendar\CalendarTest::testCalculateDaysPreviousMonth
ReflectionException: Class Calendar does not exist /Library/WebServer/Documents/calendar/src/My/CalendarBundle/Tests/Calendar/CalendarTest.php:47
<?php
namespace My\CalendarBundle\Tests\Calendar;
use My\CalendarBundle\Calendar\Calendar;
class CalendarTest
{
//this method works fine
public function testGetNextYear()
{
$this->calendar = new Calendar('12', '2012', $this->get('translator'));
$result = $this->calendar->getNextYear();
$this->assertEquals(2013, $result);
}
public function testCalculateDaysPreviousMonth()
{
$reflectionCalendar = new \ReflectionClass('Calendar'); //this is the line
$method = $reflectionCalendar->getMethod('calculateDaysPreviousMonth');
$method->setAccessible(true);
$this->assertEquals(5, $method->invokeArgs($this->calendar, array()));
}
}
Why?
Thank you in advance
You need to use the whole namespaced class name when creating your reflection method, even if you include a use statement.
new \ReflectionClass('My\CalendarBundle\Calendar\Calendar');
This is because you are passing the class name as a string to the constructor, so it doesn't know about your use statement and is looking for the class name in the global namespace.
Also, for what it's worth, you don't actually need to create a ReflectionClass, then call getMethod() on it. Rather, you can directly create a ReflectionMethod object.
new \ReflectionMethod('My\CalendarBundle\Calendar\Calendar', 'calculateDaysPreviousMonth');
That should be essentially the same, but a bit shorter.

Symfony2: Testing entity validation constraints

Does anyone have a good way to unit test an entity's validation constraints in Symfony2?
Ideally I want to have access to the Dependency Injection Container within the unit test which would then give me access to the validator service. Once I have the validator service I can run it manually:
$errors = $validator->validate($entity);
I could extend WebTestCase and then create a client to get to the container as per the docs however it doesn't feel right. The WebTestCase and client read in the docs as more of a facility to test actions as a whole and therefore it feels broken to use it to unit test an entity.
So, does anyone know how to either a) get the container or b) create the validator inside a unit test?
Ok since this got two votes I guess other people are interested.
I decided to get my shovel out and was pleasantly surprised (so far anyway) that this wasn't at all difficult to pull off.
I remembered that each Symfony2 component can be used in a stand alone mode and therefore that I could create the validator myself.
Looking at the docs at: https://github.com/symfony/Validator/blob/master/ValidatorFactory.php
I realised that since there was a ValidatorFactory it was trivial to create a validator (especially for validation done by annotations which I am, although if you look at the docblock on the page I linked above you'll also find ways to validate xml and yml).
First:
# Symfony >=2.1
use Symfony\Component\Validator\Validation;
# Symfony <2.1
use Symfony\Component\Validator\ValidatorFactory;
and then:
# Symfony >=2.1
$validator = Validation::createValidatorBuilder()->enableAnnotationMapping()->getValidator();
# Symfony <2.1
$validator = ValidatorFactory::buildDefault()->getValidator();
$errors = $validator->validate($entity);
$this->assertEquals(0, count($errors));
I hope this helps anyone else whose conscience wouldn't allow them to just use WebTestCase ;).
We end up rolling your own base test case to access the dependency container from within a test case. Here the class in question:
<?php
namespace Application\AcmeBundle\Tests;
// This assumes that this class file is located at:
// src/Application/AcmeBundle/Tests/ContainerAwareUnitTestCase.php
// with Symfony 2.0 Standard Edition layout. You may need to change it
// to fit your own file system mapping.
require_once __DIR__.'/../../../../app/AppKernel.php';
class ContainerAwareUnitTestCase extends \PHPUnit_Framework_TestCase
{
protected static $kernel;
protected static $container;
public static function setUpBeforeClass()
{
self::$kernel = new \AppKernel('dev', true);
self::$kernel->boot();
self::$container = self::$kernel->getContainer();
}
public function get($serviceId)
{
return self::$kernel->getContainer()->get($serviceId);
}
}
With this base class, you can now do this in your test methods to access the validator service:
$validator = $this->get('validator');
We decided to go with a static function instead of the class constructor but you could easily change the behavior to instantiate the kernel into the constructor directly instead of relying on the static method setUpBeforeClass provided by PHPUnit.
Also, keep in mind that each single test method in you test case won't be isolated fro, each others because the container is shared for the whole test case. Making modification to the container may have impact on you other test method but this should not be the case if you access only the validator service. However, this way, the test cases will run faster because you will not need to instantiate and boot a new kernel for each test methods.
For the sake of reference, we find inspiration for this class from this blog post. It is written in French but I prefer to give credit to whom it belongs :)
Regards,
Matt
I liked Kasheens answer, but it doesn't work for Symfony 2.3 anymore.
There are little changes:
use Symfony\Component\Validator\Validation;
and
$validator = Validation::createValidatorBuilder()->getValidator();
If you want to validate Annotations for instance, use enableAnnotationMapping() like below:
$validator = Validation::createValidatorBuilder()->enableAnnotationMapping()->getValidator();
the rest stays the same:
$errors = $validator->validate($entity);
$this->assertEquals(0, count($errors));
With Symfony 2.8, it seems that you can now use the AbstractConstraintValidatorTest class this way :
<?php
namespace AppBundle\Tests\Constraints;
use Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest;
use AppBundle\Constraints\MyConstraint;
use AppBundle\Constraints\MyConstraintValidator;
use AppBundle\Entity\MyEntity;
use Symfony\Component\Validator\Validation;
class MyConstraintValidatorTest extends AbstractConstraintValidatorTest
{
protected function getApiVersion()
{
return Validation::API_VERSION_2_5;
}
protected function createValidator()
{
return new MyConstraintValidator();
}
public function testIsValid()
{
$this->validator->validate(null, new MyEntity());
$this->assertNoViolation();
}
public function testNotValid()
{
$this->assertViolationRaised(new MyEntity(), MyConstraint::SOME_ERROR_NAME);
}
}
You have got a good sample with the IpValidatorTest class
The answer in https://stackoverflow.com/a/41884661/4560833 has to be changed a little for Symfony 4:
Use ConstraintValidatorTestCase instead of AbstractConstraintValidatorTest.
Answer (b): Create the Validator inside the Unit Test (Symfony 2.0)
If you built a Constraint and a ConstraintValidator you don't need any DI container at all.
Say for example you want to test the Type constraint from Symfony and it's TypeValidator. You can simply do the following:
use Symfony\Component\Validator\Constraints\TypeValidator;
use Symfony\Component\Validator\Constraints\Type;
class TypeValidatorTest extends \PHPUnit_Framework_TestCase
{
function testIsValid()
{
// The Validator class.
$v = new TypeValidator();
// Call the isValid() method directly and pass a
// configured Type Constraint object (options
// are passed in an associative array).
$this->assertTrue($v->isValid(5, new Type(array('type' => 'integer'))));
$this->assertFalse($v->isValid(5, new Type(array('type' => 'string'))));
}
}
With this you can check every validator you like with any constraint configuration. You neither need the ValidatorFactory nor the Symfony kernel.
Update: As #psylosss pointed out, this doesn't work in Symfony 2.5. Nor does it work in Symfony >= 2.1. The interface from ConstraintValidator got changed: isValid was renamed to validate and doesn't return a boolean anymore. Now you need an ExecutionContextInterface to initialize a ConstraintValidator which itself needs at least a GlobalExecutionContextInterface and a TranslatorInterface... So basically it's not possible anymore without way too much work.
I don't see a problem with the WebTestCase. If you don't want a client, don't create one ;) But using a possibly different service than your actual application will use, that's a potential pit fall. So personally, I've done like this:
class ProductServiceTest extends Symfony\Bundle\FrameworkBundle\Test\WebTestCase
{
/**
* Setup the kernel.
*
* #return null
*/
public function setUp()
{
$kernel = self::getKernelClass();
self::$kernel = new $kernel('dev', true);
self::$kernel->boot();
}
public function testFoo(){
$em = self::$kernel->getContainer()->get('doctrine.orm.entity_manager');
$v = self::$kernel->getContainer()->get('validator');
// ...
}
}
It's less DRY than Matt answer -- as you'll repeat the code (for each test class) and boot the kernel often (for each test method), but it's self-contained and require no extra dependencies, so it depends on your needs. Plus I got rid of the static require.
Also, you're sure to have the same services that your application is using -- not default or mock, as you boot the kernel in the environnement that you wish to test.
If people still read this one in 2023, prefer to inject the ValidatorInterface for Symfony > 3 / 4.
use Symfony\Component\Validator\Validator\ValidatorInterface;
// ...
$this->validator->validate($myEntity);

Can I create a column of nvarchar(MAX) using FluentMigrator?

Using FluentMigrator, the default creation of a Column using .AsString() results in an nvarchar(255). Is there a simple way (before I modify the FluentMigrator code) to create a column of type nvarchar(MAX)?
You could create an extension method to wrap .AsString(Int32.MaxValue) within .AsMaxString()
e.g.
internal static class MigratorExtensions
{
public static ICreateTableColumnOptionOrWithColumnSyntax AsMaxString(this ICreateTableColumnAsTypeSyntax createTableColumnAsTypeSyntax)
{
return createTableColumnAsTypeSyntax.AsString(int.MaxValue);
}
}
OK, I found it. Basically, use .AsString(Int32.MaxValue). Pity there's not a .AsMaxString() method, but I guess it's easy enough to put in...
You can use AsCustom("nvarchar(max)") and pack it to extension
If you often create columns/tables with the same settings or groups of columns, you should be creating extension methods for your migrations!
For example, nearly every one of my tables has CreatedAt and UpdatedAt DateTime columns, so I whipped up a little extension method so I can say:
Create.Table("Foos").
WithColumn("a").
WithTimestamps();
I think I created the Extension method properly ... I know it works, but FluentMigrator has a LOT of interfaces ... here it is:
public static class MigrationExtensions {
public static ICreateTableWithColumnSyntax WithTimestamps(this ICreateTableWithColumnSyntax root) {
return root.
WithColumn("CreatedAt").AsDateTime().NotNullable().
WithColumn("UpdatedAt").AsDateTime().NotNullable();
}
}
Similarly, nearly every one of my tables has an int primary key called 'Id', so I think I'm going to add Table.CreateWithId("Foos") to always add that Id for me. Not sure ... I actually just started using FluentMigrator today, but you should always be refactoring when possible!
NOTE: If you do make helper/extension methods for your migrations, you should never ever ever change what those methods do. If you do, someone could try running your migrations and things could explode because the helper methods you used to create Migration #1 works differently now than they did earlier.
Here is the code for creating columns incase it helps you create helper methods: https://github.com/schambers/fluentmigrator/blob/master/src/FluentMigrator/Builders/Create/Column/CreateColumnExpressionBuilder.cs
How about extending like this:
public static class StringMaxMigratorExtensions
{
public static ICreateTableColumnOptionOrWithColumnSyntax AsStringMax(this ICreateTableColumnAsTypeSyntax createTableColumnAsTypeSyntax)
{
return createTableColumnAsTypeSyntax.AsCustom("nvarchar(max)");
}
public static IAlterColumnOptionSyntax AsStringMax(this IAlterColumnAsTypeSyntax alterColumnAsTypeSyntax)
{
return alterColumnAsTypeSyntax.AsCustom("nvarchar(max)");
}
}

Resources