SonataAdminBundle error" Call to a member function get() on null "when override controller - sonata

I wish override controller in Sonata Admin, but it returns:
Call to a member function get() on null
Code in services.yml:
app.admin.model:
class: AppBundle\Admin\ModelAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: Model,label: Model }
arguments:
- null
- AppBundle\Entity\Model
- AppBundle\Controller\ModelController
public: true
Code in controller:
namespace AppBundle\Controller;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Sonata\AdminBundle\Controller\CRUDController as Controller;
use Symfony\Component\HttpFoundation\Response;
class ModelController extends Controller {
/**
*
* #param int|string|null $id
*
* #throws NotFoundHttpException
*
* #return Response
*/
public function showAction($id = null) {
$request = $this->getRequest();
$id = $request->get($this->admin->getIdParameter());
$object = $this->admin->getObject($id);
if (!$object) {
throw $this->createNotFoundException(sprintf('unable to find the object with id : %s', $id));
}
$this->admin->checkAccess('show', $object);
$preResponse = $this->preShow($request, $object);
if (null !== $preResponse) {
return $preResponse;
}
$this->admin->setSubject($object);
return $this->render($this->admin->getTemplate('show'), array(
'action' => 'show',
'object' => $object,
'elements' => $this->admin->getShow(),
), null);
}
}
It's a simple copy/paste of vendor for the moment, and when I return in ma view, I get this error:
CRITICAL
16:31:37
php Call to a member function get() on null
CRITICAL
16:31:37
request Uncaught PHP Exception Symfony\Component\Debug\Exception\FatalThrowableError: "Call to a member function get() on null" at /var/www/html/acianovintra2/vendor/sonata-project/admin-bundle/src/Controller/CRUDController.php line 988
Can you help me please?

Related

How does Symfony controller as service work with __invoke method?

For example, in the following code:
/**
* #Route("/patients", service="bundle1.controller.patient.index")
*/
final class IndexController
{
private $router;
private $formFactory;
private $templating;
private $patientFinder;
public function __construct(RouterInterface $router, FormFactoryInterface $formFactory, EngineInterface $templating, PatientFinder $patientFinder)
{
$this->router = $router;
$this->formFactory = $formFactory;
$this->templating = $templating;
$this->patientFinder = $patientFinder;
}
/**
* #Route("", name="patients_index")
*/
public function __invoke(Request $request) : Response
{
$form = $this->formFactory->create(PatientFilterType::class, null, [
'action' => $this->router->generate('patients_index'),
'method' => Request::METHOD_GET,
]);
$form->handleRequest($request);
$patients = $this->patientFinder->matching($form->getData() ?: []);
return $this->templating->renderResponse('patient/index.html.twig', [
'form' => $form->createView(),
'patients' => $patients,
]);
}
}
Why is there a route annotation for __invoke that is empty?.
What is the lifecycle of this controller? I mean, when does Symfony creates the object and when executes the class to make use of __invoke?
Empty #Route annotation means that there is nothing after main route of class which is /patients. __invoke is a magic PHP method that is executed when you call your class as a function (without providing any method).
So __invoke method is executed when you hit the route /patients or when you call your service from any code.

zend 3, graphQL - how to create model instance using factory in Types class

Currently I have such Types.php:
namespace Application\GraphQL;
use Application\GraphQL\Type\NodeType;
use Application\GraphQL\Type\QueryType;
use GraphQL\Type\Definition\NonNull;
use GraphQL\Type\Definition\Type;
use Application\GraphQL\Type\PersonType;
/**
* Class Types
*
* Acts as a registry and factory for your types.
*
* As simplistic as possible for the sake of clarity of this example.
* Your own may be more dynamic (or even code-generated).
*
* #package GraphQL\Examples\Blog
*/
class Types
{
private static $query;
private static $person;
private static $node;
public static function person()
{
return self::$person ?: (self::$person = new PersonType());
}
/**
* #return QueryType
*/
public static function query()
{
return self::$query ?: (self::$query = new QueryType());
}
/**
* #return NodeType
*/
public static function node()
{
return self::$node ?: (self::$node = new NodeType());
}
/**
* #return \GraphQL\Type\Definition\IDType
*/
public static function id()
{
return Type::id();
}
/**
* #return \GraphQL\Type\Definition\StringType
*/
public static function string()
{
return Type::string();
}
/**
* #param Type $type
* #return NonNull
*/
public static function nonNull($type)
{
return new NonNull($type);
}
}
In query() function it creates QueryType instance. I added to QueryType constructor PersonTable model class so that it could query persons from database.
QueryType.php
public function __construct(PersonTable $table)
{
$config = [
'name' => 'Query',
'fields' => [
'person' => [
'type' => Types::person(),
'description' => 'Returns person by id',
'args' => [
'id' => Types::nonNull(Types::id())
]
],
'hello' => Type::string()
],
'resolveField' => function($val, $args, $context, ResolveInfo $info) {
return $this->{$info->fieldName}($val, $args, $context, $info);
}
];
$this->table = $table;
parent::__construct($config);
}
I have set up factories in module\Application\src\Module.php:
/**
* #link http://github.com/zendframework/ZendSkeletonApplication for the canonical source repository
* #copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* #license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Application;
use Application\Model\PersonTable;
use Application\Model\Person;
use Zend\Db\ResultSet\ResultSet;
use Zend\Db\TableGateway\TableGateway;
class Module
{
const VERSION = '3.0.2dev';
public function getConfig()
{
return include __DIR__ . '/../config/module.config.php';
}
// Add this method:
public function getServiceConfig()
{
return array(
'factories' => array(
'Application\Model\PersonTable' => function($sm) {
$tableGateway = $sm->get('PersonTableGateway');
$table = new PersonTable($tableGateway);
return $table;
},
'PersonTableGateway' => function ($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Person());
return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
},
),
);
}
}
I am doing by this example which does not have any framework:
https://github.com/webonyx/graphql-php/tree/master/examples/01-blog
So the question is - how do I create queryType instance with injected PersonTable instance? I should somehow get from the factory the PersonTable instance but I do not understand how.
Update:
I decided to try to inject QueryType into the controller. Created such function:
public function __construct(QueryType $queryType)
{
$this->queryType = $queryType;
}
Now module\Application\src\Module.php getServiceConfig looks like this:
public function getServiceConfig()
{
return array(
'factories' => array(
'Application\Model\PersonTable' => function($sm) {
$tableGateway = $sm->get('PersonTableGateway');
$table = new PersonTable($tableGateway);
return $table;
},
'PersonTableGateway' => function ($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Person());
return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
},
QueryType::class => function ($sm) {
return new QueryType($sm->get(PersonTable::class));
}
// when putting in namespace does not find??????????
//QueryType::class => Application\GraphQL\Type\Factories\QueryTypeFactory::class
//QueryType::class => \QueryTypeFactory::class
),
);
}
But I get error:
Catchable fatal error: Argument 1 passed to Application\Controller\IndexController::__construct() must be an instance of Application\GraphQL\Type\QueryType, none given, called in E:\projektai\php projektai\htdocs\graphQL_zend_3\vendor\zendframework\zend-servicemanager\src\Factory\InvokableFactory.php on line 32 and defined in E:\projektai\php projektai\htdocs\graphQL_zend_3\module\Application\src\Controller\IndexController.p
How can none be given if I configured in that function?
If I could inject into the controller, then I plan to do like this:
$schema = new Schema([
//'query' => Types::query()
'query' => $this->queryType
]);
So I would not need to call query() function which return the QueryType instance anyway.
And then PersonTable would be automatically injected into QueryType class.
Update:
I had created the factory, similar as in the asnswer:
class QueryTypeFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
return new QueryType($container->get(PersonTable::class));
}
}
In the IndexController I have constructor:
public function __construct(QueryType $queryType)
{
$this->queryType = $queryType;
}
In the Module.php I use this factory:
public function getServiceConfig()
{
return array(
'factories' => array(
'Application\Model\PersonTable' => function($sm) {
$tableGateway = $sm->get('PersonTableGateway');
$table = new PersonTable($tableGateway);
return $table;
},
'PersonTableGateway' => function ($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Person());
return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
},
// QueryType::class => function ($sm) {
// //return new QueryType($sm->get(PersonTable::class));
//
// }
//QueryType::class => Application\GraphQL\Type\Factories\QueryTypeFactory::class
//QueryType::class => \QueryTypeFactory::class
QueryType::class => QueryTypeFactory::class
),
);
}
It simply does not work, I get error:
Catchable fatal error: Argument 1 passed to Application\Controller\IndexController::__construct() must be an instance of Application\GraphQL\Type\QueryType, none given, called in E:\projektai\php projektai\htdocs\graphQL_zend_3\vendor\zendframework\zend-servicemanager\src\Factory\InvokableFactory.php on line 32 and defined in E:\projektai\php projektai\htdocs\graphQL_zend_3\module\Application\src\Controller\IndexController.php on line
I also tried this way:
$queryTypeFactory = new QueryTypeFactory();
// GraphQL schema to be passed to query executor:
$schema = new Schema([
//'query' => Types::query()
//'query' => $this->queryType
// 'query' => $queryType
'query' => $queryTypeFactory()
]);
But the $queryTypeFactory() needs parameter $container. Which is not what I want, I guess. I should be able to create an instance without passing parameters.
I hope it is ok to use QueryType::class in the factories array as key. It will create with full name space which is set:
use Application\GraphQL\Type\QueryType;
And in index controller I also call that use statement.
<?php
namespace Application\Service\Factory;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
use Application\Service\CurrencyConverter;
use Application\Service\PurchaseManager;
/**
* This is the factory for PurchaseManager service. Its purpose is to instantiate the
* service and inject its dependencies.
*/
class PurchaseManagerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container,
$requestedName, array $options = null)
{
// Get CurrencyConverter service from the service manager.
$currencyConverter = $container->get(CurrencyConverter::class);
// Instantiate the service and inject dependencies.
return new PurchaseManager($currencyConverter);
}
}
In the code above we have the PurchaseManagerFactory class which implements the Zend\ServiceManager\Factory\FactoryInterface interface. The factory class has the __invoke() method whose goal is to instantiate the object. This method has the $container argument which is the service manager. You can use $container to retrieve services from service manager and pass them to the constructor method of the service being instantiated.

Symfony2 pass two variables from the service to the Controller $slug

To clean up my controller code I want to move the "newPostAction" to a service. The problem I get is that now I cannot pass as a result of the funtion in the service two variables to the controller. I use the function to create a form, and the get the slug from the form's post and render it. I do not know how to pass it to the controller. I tried using the "list()" function but it does not get the info right.
How can I call the pos's "slug" from inside the controller?
Here is the controller code:
/**
* #param Request $request
* #return array
*
* #Route("/new/_post", name="_blog_backend_post_new")
* #Template("BlogBundle:Backend/Post:new.html.twig")
*/
public function newPostAction(Request $request)
{
$form_post = $this->getPostManager()->createPost($request);
$slug_post = ¿How do I get it from inside the createPost()?;
if (true === $form_post)
{
$this->get('session')->getFlashBag()->add('success', 'Your post was submitted successfully');
return $this->redirect($this->generateUrl('blog_blog_post_show', array('slug' => $slug_post)));
}
return array(
'post_slug' => $slug_post,
'form_post' => $form_post->createView()
);
}
Here is the PostManager service to create the new post entity:
/**
* Create and validate a new Post
*
* #param Request $request
* #return bool|FormInterface
*/
public function createPost (Request $request)
{
$post = new Post();
$post->setAuthor($this->um->getloggedUser());
$form_post = $this->formFactory->create(new PostType(), $post);
$form_post->handleRequest($request);
$slug_post = $post->getSlug();
if ($form_post->isValid())
{
$this->em->persist($post);
$this->em->flush();
return true;
}
return $form_post;
}
You just need to return an array from the service and access the values from the controller.
UPDATE
Some changes need to be made to your code in order to get things to work.
Explanation: when the form is valid, the previous code (I deleted it) returned true therefore $ret["form_post"] didn't make sense because $ret was not an array. It surprises me that it didn't throw you an error.
Anyway, that could explain why Doctrine didn't persist your entity. Talking about the redirection, the error could be due to the same reason. $ret was true (a boolean) and $ret["form_slug"] didn't make sense either.
I hope this fixes the problems. Please, let me know if it works.
Service
public function createPost (Request $request)
{
$post = new Post();
$post->setAuthor($this->um->getloggedUser());
$form_post = $this->formFactory->create(new PostType(), $post);
$form_post->handleRequest($request);
$slug_post = $post->getSlug();
if ($form_post->isValid())
{
$this->em->persist($post);
$this->em->flush();
return array("form_post" => true, "slug_post" => $slug_post);;
}
return array("form_post" => $form_post, "slug_post" => $slug_post);
}
Controller:
public function newPostAction(Request $request)
{
$ret = $this->getPostManager()->createPost($request);
$form_post = $ret["form_post"];
$slug_post = $ret["slug_post"];
if (true === $form_post)
{
$this->get('session')->getFlashBag()->add('success', 'Your post was submitted successfully');
return $this->redirect($this->generateUrl('blog_blog_post_show', array('slug' => $slug_post)));
}
return array(
'post_slug' => $slug_post,
'form_post' => $form_post->createView()
);
}

Testing symfony 2 forms

I develop new type, but I don't know how I can test it.
Assert annotation is not load and validations is not called.
Could any one please help me?
class BarcodeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->
add('price');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Bundles\MyBundle\Form\Model\Barcode',
'intention' => 'enable_barcode',
));
}
public function getName()
{
return 'enable_barcode';
}
}
A have following model for storing form data.
namepspace Bundles\MyBundle\Form\Model;
class Barcode
{
/**
* #Assert\Range(
* min = "100",
* max = "100000",
* minMessage = "...",
* maxMessage = "..."
* )
*/
public $price;
}
I develop some test like this, the form didn't get valid data but it is valid! (Because annotation is not applied)
I try adding ValidatorExtension but I dont know how can I set constructor paramaters
function test...()
{
$field = $this->factory->createNamed('name', 'barcode');
$field->bind(
array(
'price' => 'hello',
));
$data = $field->getData();
$this->assertTrue($field->isValid()); // Must not be valid
}
Not sure why you need to unit-test the form. Cant You unit test validation of Your entity and cover controller with your expected output?
While testing validation of entity You could use something like this:
public function testIncorrectValuesOfUsernameWhileCallingValidation()
{
$v = \Symfony\Component\Validator\ValidatorFactory::buildDefault();
$validator = $v->getValidator();
$not_valid = array(
'as', '1234567890_234567890_234567890_234567890_dadadwadwad231',
"tab\t", "newline\n",
"Iñtërnâtiônàlizætiøn hasn't happened to ", 'trśżź',
'semicolon;', 'quote"', 'tick\'', 'backtick`', 'percent%', 'plus+', 'space ', 'mich #l'
);
foreach ($not_valid as $key) {
$violations = $validator->validatePropertyValue("\Brillante\SampleBundle\Entity\User", "username", $key);
$this->assertGreaterThan(0, count($violations) ,"dissalow username to be ($key)");
}
}
Functional test. Given that you generate a CRUD with app/console doctrine:generate:crud with routing=/ss/barcode, and given that maxMessage="Too high" you can:
class BarcodeControllerTest extends WebTestCase
{
public function testValidator()
{
$client = static::createClient();
$crawler = $client->request('GET', '/ss/barcode/new');
$this->assertTrue(200 === $client->getResponse()->getStatusCode());
// Fill in the form and submit it
$form = $crawler->selectButton('Create')->form(array(
'ss_bundle_eavbundle_barcodetype[price]' => '12',
));
$client->submit($form);
$crawler = $client->followRedirect();
// Check data in the show view
$this->assertTrue($crawler->filter('td:contains("12")')->count() > 0);
// Edit the entity
$crawler = $client->click($crawler->selectLink('Edit')->link());
/* force validator response: */
$form = $crawler->selectButton('Edit')->form(array(
'ss_bundle_eavbundle_barcodetype[price]' => '1002',
));
$crawler = $client->submit($form);
// Check the element contains the maxMessage:
$this->assertTrue($crawler->filter('ul li:contains("Too high")')->count() > 0);
}
}
Include this line must be in Model and try it after include look like your model.
/* Include the required validators */
use Symfony\Component\Validator\Constraints as Assert;
namespace Bundles\MyBundle\Form\Model;
class Barcode
{
/**
* #Assert\Range(
* min = "100",
* max = "100000",
* minMessage = "min message here",
* maxMessage = "max message here"
* )
*/
public $price;
}

Symfony forms. File upload

Trying to manage file upload with Entity, but i get this error:
Fatal error: Call to a member function move() on a non-object in /home/projectpath/src/BS/MyBundle/Entity/Items.php on line 327 Call Stack: 0.0002 333264 1. {main}() /home/projectpath/web/app_dev.php:0 0.0450 1158160...
Here's the entity class:
namespace BS\BackstretcherBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;
/**
* MB\MyBundle\Entity\Items
*
* #ORM\Table(name="items")
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Items
{
private $filenameForRemove;
/**
* #Assert\File(maxSize="60000000")
*/
public $file;
...
protected function getUploadDir()
{
return 'images/items/';
}
protected function getUploadRootDir()
{
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
public function getWebPath()
{
return null === $this->file ? null : $this->getUploadDir().'/'.$this->getNameEn();
}
public function getAbsolutePath()
{
return null === $this->file ? null : $this->getUploadRootDir().'/'.$this->getNameEn().'.jpg';
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if (null !== $this->file)
{
$this->file = $this->getId() .'.'. $this->file->guessExtension();
}
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
if (null === $this->file)
{
return;
}
$this->file->move($this->getUploadRootDir(), $this->file);
unset($this->file);
}
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
if ($file = $this->getAbsolutePath())
{
unlink($file);
}
}
And the controller:
public function new_productAction(Request $request)
{
$product = new Items();
$product->setPrice(0);
$form = $this->createFormBuilder($product)
->add('Type', 'choice', array(
'choices' => array('1' => 'Product', '0' => 'Article'),
'required' => false,))
->add('Price', 'number')
->add('nameEn', 'text')
->add('file', 'file', array('label' => 'Image', 'required' => true))
->getForm();
if ($request->getMethod() == 'POST')
{
if ($form->isValid())
{
$form->bindRequest($request);
$em = $this->getDoctrine()->getEntityManager();
$em->persist($product);
$em->flush();
return new Response('<html><body>Success!</body></html>');
}
}
return $this->render('MyBundle:Default:admin_page.html.twig', array(
'form' => $form->createView(),
));
}
Symfony version: 2.1.0
Check your php.ini file and make sure both the post_max_size AND upload_max_filesize are set sufficiently large.
I don't suppose duke_nukem is worried about this anymore, 6 months down the line, but if someone else comes across this question, I was having the exact same problem and got a great answer to it here:
Error with file upload in symfony 2
Looks like duke_nukem and I made the same mistake. The preUpload() method should read:
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if (null !== $this->file)
{
$this->path = $this->getId() .'.'. $this->file->guessExtension();
}
}
The present code converts $this->file to a string, causing the error. The path should actually be assigned to $this->path.
Sybio in the other question figured this out, not me. I just want to spread the love.
it's weird
your code is wrong in your controller. You have to bind your request to your form before validation. After that, you can retrieve your data
if ($request->getMethod() == 'POST')
{
//Note: bindRequest is now deprecated
$form->bind($request);
if ($form->isValid())
{
//retrieve your model hydrated with your form values
$product = $form->getData();
//has upload file ?
if($product->getFile() instanceof UploadedFile){
//you can do your upload logic here wihtout doctrine event if you want
}
$em = $this->getDoctrine()->getEntityManager();
$em->persist($product);
$em->flush();
return new Response('<html><body>Success!</body></html>');
}
}