Reputation: 185
im fairly new to Mac and Symfony in general so sorry if im missing any basic knowledge
basically trying to create a form using a php class, here its below
<?php
namespace TeamRock\ApplicationBundle\entity;
class Person{
protected $email;
protected $fullname;
public function getEmail(){
return $this->$email;
}
public function setEmail($email){
return $this->$email;
}
public function getFullname(){
return $this ->$fullname;
}
public function setFullname($fullname){
return $this -> $fullname;
}
}
?>
i then have my class
<?php
namespace TeamRock\ApplicationBundle\form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class personType extends AbstractType{
public function buildForm(FormBuilderInterface $builder, array $options){
$builder->add ('email','email')->add ('name','text')->add('submit','submit');
}
public function getName(){
return 'person';
}
}
?>
and then on the main controller
<?php
namespace TeamRock\ApplicationBundle\Controllers\Calvin;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use TeamRock\ApplicationBundle\entity\Person;
use TeamRock\ApplicationBundle\form\PersonType;
class Homepage
{
public function __invoke(Request $request)
{
$person = new person();
$form = $this->createForm(new PersonType(), $person);
return new Response("Hello, world!", Response::HTTP_OK, array('form'=>$form->createView()));
}
}
?>
And the error i keep getting is
Attempted to call method "createForm" on class "TeamRock\ApplicationBundle\Controllers\Calvin\Homepage".
500 Internal Server Error - UndefinedMethodException
Any help is appreciated guys, as i said im pretty new to php and symfony and its a big learning curve so any helps or pointers are appreciated
ive been watching this guys videos just for reference Andrew Perkins Symfony Tutorials
thanks again for any help in advance!
Upvotes: 1
Views: 3015
Reputation: 48893
How to use a controller as a service? The documentation is always a good place to start:http://symfony.com/doc/current/cookbook/controller/service.html
In the case of this question, the controller use a createForm method which happens to be defined in:
namespace Symfony\Bundle\FrameworkBundle\Controller;
use Symfony\Component\DependencyInjection\ContainerAware;
class Controller extends ContainerAware
{
public function createForm($type, $data = null, array $options = array())
{
return $this->container->get('form.factory')->create($type, $data, $options);
}
As you can see, the createForm method relies on the container object to access the form.factory object. How does the container get injected?
namespace Symfony\Component\DependencyInjection;
abstract class ContainerAware implements ContainerAwareInterface
{
protected $container;
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
So somewhere along the line we need to call $controller->setContainer. If we were using controllers in the normal fashion (i.e. not as services) then setContainer gets called in the Symfony\Component\HttpKernel\HttpKernel::handleRaw() method. But since we are defining the controller as a service then it is up to us to inject the container:
# services.yml
homepage__controller:
class: Whatever\Homepage
calls:
- [setContainer, ['@service_container']]
So adding the setContainer call to your service definition should give you working code. Though adding createView directly to your response might not give you the desired results. It certainly won't give you html. But that is a different issue.
The real question of course is why bother defining the controller as a service. In almost all cases, the answer is that you should not. It used to be recommended for third party bundles but not so much any more. If you look at the FOSUserBundle controllers you see quite a bit of duplicate code just because they blindly followed the controller as a service rule.
There are times when it makes sense. I try to keep controllers as slim as possible and only inject the services that are actually needed. In this question, the only service the controller needs is the form factory so it should be the only one injected. And you should no longer extend the base controller class.
class HomepageController
{
private $formFactory;
public function setFormFactory($formFactory)
{
$this->formFactory = $formFactory;
}
public function __invoke(Request $request)
{
$person = new Person();
$form = $this->formFactory->create(new PersonType(), $person);
return new Response("Hello, world!", Response::HTTP_OK, array('form'=>$form->createView()));
}
}
services.yml
my__controller:
class: Whatever\HomepageController
calls:
- [setFormFactory, ['@form.factory']]
I like to use setter injection instead of construct inject for these sorts of standard services. I save the construct for my own custom services. But you could certainly use arguments: ['@form.factory'] if you wanted.
Finally though, if you are just learning the framework as well as PHP OOP then just follow the examples in the documentation. There is plenty to learn without going down these sorts of rabbit holes.
Upvotes: 2