Hi I want to render a form in symFony2 with dynamic fields added to it.
So I have wrote code in controller as:
public function getDataAction(){
$handler = new AnotherClass();
$object = $handler->getForm($getAdditionalData);
}
and "AnotherClass" defined as following:
class AnotherClass extends Controller implements requiredInterface{
public function getForm($formData){
//Here i want to write Logic to render a dynamic form with dynamic fields
$form = $this->createFormBuilder($formData)
->setAction($this->generateUrl('me_route_go'))
// Set form field of additional data
foreach ($formData as $k => $v) {
$form->add($k, 'text');
}
//Create form and submit button
$form = $form->add('submit', 'submit')->add('Cancel', 'reset')->getForm();
$form = $form->getForm();
}
}
}
But here I am getting following error:
Error: Call to a member function get() on a non-object.
return $this->container->get('form.factory')->createBuilder($type, $data, $options);
Please suggest what could be the issue.
thanks in advance..
Your controller AnotherClass requires the Dependency Injection Container since you are extending the base Controller class, you have to set it after you instantiate it :
public function getDataAction(){
$handler = new AnotherClass();
$handler->setContainer($this->container);
$object = $handler->getForm($getAdditionalData);
}
You can also create it as a service :
services.yml
name.of.your.service:
class: Path\To\AnotherClass
calls:
- [setContainer, [ "#service_container" ]]
And then :
public function getDataAction(){
$handler = $this->get('name.of.your.service');
$object = $handler->getForm($getAdditionalData);
}
Related
When trying to do a simple search method with Algolia. The search method will query Algolia to get matching results and then will create a doctrine collection. I get this error:
I injected my indexManager in my controller :
class ProductController extends Controller
{
protected $indexManager;
public function __construct(IndexManagerInterface $indexingManager)
{
$this->indexManager = $indexingManager;
}
public function displayAction(Request $request) {
$em = $this->getDoctrine()->getManagerForClass(Product::class);
$posts = $this->indexManager->search('query', Product::class, $em);
return $this->render('ProductBundle:Default:postDisplay.html.twig', array(
'posts' => $posts));
}
}
Can you make sure the autowire feature is set to true?
https://symfony.com/doc/current/service_container/autowiring.html
Symfony should resolve the dependency automatically.
I am creating a Symphony application that browses different data sources.
the controller that I created knows too much about the data source but the application is designed in a way to not expect that.
The data source could be DB, JSON or XML.
is there any way to implement interfaces to do that?
My controller knows the location of the XML file, and browse different data seperatly. I want to do it in one action.
That's my current controller ;
public function searchAction(Request $request) {
if ($request->getMethod() == 'POST') {
$search_for = $request->get('search');
//getting the searched products from the database
$repository = $this->getDoctrine()->getRepository('TyreTyreBundle:Products');
$query = $repository->createQueryBuilder('u')
->where("u.name LIKE '%".$search_for."%' or u.manufacturer LIKE '%".$search_for."%'")
->getQuery();
$results = $query->getResult();
//adding the XML file products
$file_url = "bundles/tyretyre/xml/products.xml";
//Convert the products.XML file into a SimpleXMLElement object
$simpleXMLElementObject = simplexml_load_file($file_url);
$i=0;
//the array where will saved the searched products from the XML file
$xml_result = [];
//looping the xml object to find matching results
while ($simpleXMLElementObject->product[$i]) {
//first we will convert to lower case both searched item and the tested name
if (strstr(strtolower($simpleXMLElementObject->product[$i]->name),strtolower($search_for))){
//push that element into the array to display it later in the twig file
array_push($xml_result, $simpleXMLElementObject->product[$i]);
}
$i++;
}
//end of products searching from the XML source
//display the detail page with passing the DB result and XML result arrays
return $this->render('TyreTyreBundle:Default:detail.html.twig', array('results' => $results,'xml_result' => $xml_result));
}
return $this->render('TyreTyreBundle:Default:search.html.twig');
}
My products entity :
namespace Tyre\TyreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Products
*/
class Products
{
//some getter and setters and private attribute
}
EDIT following VolCh solution,
I registered the service as following (I think I am doing it wrong) in /src/Tyre/TyreBundle/Resources/config/services.yml:
services:
Tyre\TyreBundle\Repository\DoctrineProductRepository:
class: Tyre\TyreBundle\Repository\DoctrineProductRepository
Tyre\TyreBundle\Repository\ProductRepositoryInterface:
class: Tyre\TyreBundle\Repository\ProductRepositoryInterface
But then I get the following
ContextErrorException: Notice: Undefined index: in
/home/smiles/Documents/tyre/src/Tyre/TyreBundle/Controller/DefaultController.php
line 56
Line 56: is this line : $serviceName = $repositoryMap[$request->get('db')];
You could:
declare \TyreTyreBundle\ProductRepository interface (or ProductDataSource if you wish) with method ->search(string $needle): array (or some DTO)
implement it in DoctrinreProductRepository, XmlProductRepository, JsonProductRepository as service with constructor injection of \Doctrine\EntityRepository, xml-filename, json-filename
get properly repository from container in actions
(optional) create ProductRepositoryFactory whith createFor('db|xml|json') method and pass type to controller as part of route like '/datasource/{db|xml|json}' or request parameter like datasource?type=db'and create properly repository in one common action
Added:
Example (proof of concept, don't use, php7+):
src/TyreBundle/Repository/ProductRepositoryInterface.php
namespace Tyre\TyreBundle\Repository;
interface ProductRepositoryInterface
{
function search(string $needle): array;
}
src/TyreBundle/Repository/DoctrineProductRepository.php
namespace Tyre\TyreBundle\Repository;
class DoctrineProductRepository implements ProductRepositoryInterface
{
public function __constructor(EntityManager $em)
{
$this->em = $em;
}
public function search(string $needle): array
{
$repository = $this->em->getRepository('TyreTyreBundle:Products');
$query = $repository->createQueryBuilder('u')
->where("u.name LIKE '%".$needle."%' or u.manufacturer LIKE '%".$needle."%'")
->getQuery();
return $query->getArrayResult();
}
}
src/TyreBundle/Repository/XmlProductRepository.php
src/TyreBundle/Repository/JsonProductRepository.php
controller
public function searchAction(Request $request)
{
$repositoryMap = [
'db' => DoctrineProductRepository::class,
'xml' => XmlProductRepository::class,
'json' => JsonProductRepository::class,
];
$serviceName = $repositoryMap[$request->get('type')];
/** #var ProductRepositoryInterface */
$repository = $this->get($serviceName);
$results = $repository->search($request->get('serxh_for'));
return $this->render('TyreTyreBundle:Default:detail.html.twig', array('results' => $results));
}
also you should register Repository classes as services with their names.
Use case
I' m trying to reuse sonata_type_model_list in the front admin of my website by defining this in my buildForm method of my Entity FormType :
[...]
->add('position', 'sonata_type_model_list', array(
'model_manager' => $categoryManager,
))
[...]
However I can't use
$categoryAdmin = $this
->getConfigurationPool()
->getAdminByClass("\\Application\\Sonata\\ClassificationBundle\\Entity\\Category");
As I need to be in an AdminClass to use getConfigurationPool().
If anyone knows how to use getConfigurationPool() outside of an AdminClass or do you know how to declare sonata_type_model_list in order to use it outside of an admin class ?
You need to inject the admin pool in your form type.
Here is an example:
#services.yml
blast_base_entities.form.type.my:
class: Blast\BaseEntitiesBundle\Form\Type\MyformType
tags:
- { name: form.type, alias: blast_search_index_autocomplete }
arguments: [#sonata.admin.pool]
And in the form type:
use Sonata\AdminBundle\Admin\Pool
Class MyFormType
{
private $adminPool;
public function construct(Pool $adminPool)
{
$this->adminPool = $adminPool;
}
}
Then you can retrieve admins
$this->adminPool->getAdminByClass('Foo');
As writing in the doc, maybe you can use the sonata.admin.pool service.
$configurationPool = $this->container->get('sonata.admin.pool');
Solution
Thanks to #pbenard & #Mawcel for their answers, I combined them to achieve my goal as follow:
In my controller I create my form and inject $this->container->get('sonata.admin.pool') to it like so :
$form = $this->createForm(
new FormType($this->container->get('sonata.admin.pool')),
$entity,
$options
);
In my formType, I added the property $adminPool && the __construct method:
private $adminPool;
public function __construct(Pool $adminPool) {
$this->adminPool = $adminPool;
}
So I can now access the adminPool from the buildForm method:
public function buildForm(FormBuilderInterface $builder, array $options)
{
[...]
$categoryAdmin = $this
->getConfigurationPool()
->getAdminByClass("\\Application\\Sonata\\ClassificationBundle\\Entity\\Category" );
[...]
}
I have a controller that renders a form that is suppose to have a dropdown with titles mapped against a client_user entity. Below is code I use in my controller to create the form:
$builder = $this->get(form.factory);
$em = $this->get('doctrine.entity_manager');
$form = $builder->createBuilder(new ClientUserType($em), new ClientUser())->getForm();
Below is my ClientUserType class with a constructor that I pass the entity manager on:
<?php
namespace Application\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
class ClientUserType extends AbstractType
{
protected $entityManager;
public function __construct($entityManager)
{
$this->entityManager = $entityManager;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', EntityType::class, array(
'class' => '\\Application\\Model\\Entity\\Title',
'em' => $this->entityManager
))
->add('name')
->add('surname')
->add('contact')
->add('email');
}
public function getName()
{
return 'client_user_form';
}
}
I keep on getting this catchable fatal error below and have no idea what I need to do in order to get a dropdown with titles from a database with doctrine.
Catchable fatal error: Argument 1 passed to Symfony\Bridge\Doctrine\Form\Type\DoctrineType::__construct() must be an instance of Doctrine\Common\Persistence\ManagerRegistry, none given, called in D:\web\playground-solutions\vendor\symfony\form\FormRegistry.php on line 90 and defined in D:\web\playground-solutions\vendor\symfony\doctrine-bridge\Form\Type\DoctrineType.php on line 111
Reading from that error I have no idea where I need to create a new instance of ManagerRegistry registry as it appears that the entity manager does not work. I am also thinking perhaps I need to get the ManagerRegistry straight from the entity manager itself.
Can someone please help explain the simplest way to get this to work? What could I be missing?
Seems that doctrine-bridge form component is not configured.
Add class
namespace Your\Namespace;
use Doctrine\Common\Persistence\AbstractManagerRegistry;
use Silex\Application;
class ManagerRegistry extends AbstractManagerRegistry
{
protected $container;
protected function getService($name)
{
return $this->container[$name];
}
protected function resetService($name)
{
unset($this->container[$name]);
}
public function getAliasNamespace($alias)
{
throw new \BadMethodCallException('Namespace aliases not supported.');
}
public function setContainer(Application $container)
{
$this->container = $container;
}
}
and configure doctrine-bridge form component
$application->register(new Silex\Provider\FormServiceProvider(), []);
$application->extend('form.extensions', function($extensions, $application) {
if (isset($application['form.doctrine.bridge.included'])) return $extensions;
$application['form.doctrine.bridge.included'] = 1;
$mr = new Your\Namespace\ManagerRegistry(
null, array(), array('em'), null, null, '\\Doctrine\\ORM\\Proxy\\Proxy'
);
$mr->setContainer($application);
$extensions[] = new \Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension($mr);
return $extensions;
});
array('em') - em is key for entity manager in $application
For others that may find this: If you want to use the EntityType and you're not using a framework at all, you need to add the DoctrineOrmExtension to your FormFactoryBuilder like so:
$managerRegistry = new myManagerRegistry(
'myManager',
array('connection'),
array('em'),
'connection',
'em',
\Doctrine\ORM\Proxy\Proxy::class
);
// Setup your Manager Registry or whatever...
$doctrineOrmExtension = new DoctrineOrmExtension($managerRegistry);
$builder->addExtension($doctrineOrmExtension);
When you use EntityType, myManagerRegistry#getService($name) will be called. $name is the name of the service it needs ('em' or 'connection') and it needs to return the Doctrine entity manager or the Doctrine database connection, respectively.
In your controller, try to call the service like that:
$em = $this->get('doctrine.orm.entity_manager');
Hope it will help you.
Edit:
Sorry, I thought you was on Symfony... I have too quickly read...
I want to create a form via form factory through a service in Symfony 2.3, but I couldn't be able to reproduce it.
My services.yml looks like this:
user.form.myservice:
factory_method: createNamed
factory_service: form.factory
class: Symfony\Component\Form\Form
arguments: [my_form_type_name]
user.form.myservice.type:
class: XxX\MyBundle\Form\Type\myEntityType
arguments: [null]
tags:
- { name: form.type, alias: my_form_type_name }
This is my form type definition (myEntityType)
class MyEntityType extends AbstractType
{
private $class;
public function __construct($class = null)
{
$this->class = (isset($class)) ? $class : 'XxX\MyBundle\Entity\myEntity';
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('first_name', 'text')
->add('last_name', 'text');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => $this->class
));
}
public function getName()
{
return 'my_form_type_name';
}
}
In my controller, when I try to create the form:
$formFactory = $this->container->get('user.form.myservice');
$myForm = $formFactory->createForm();
It says:
FatalErrorException: Error: Call to undefined method Symfony\Component\Form\Form::createForm() in ...\proj1\src\XxX\MyBundle\Controller\ProfileController.php line 134
Any help please??
Thanks!
First of all you're not passing any arguments to the constructor of MyEntityType. You also used createForm method in a wrong way. It expects followigng arguments: $type, $data = null, array $options = array()`.
To get this thing work, you should specify type:
$form = $this->createForm($this->get('user.form.myservice'), new YourEntity());
Your factory service user.form.myservice returned the FormInterface instance (Symfony\Component\Form\FormFactory::createNamed) and your can call the method createForm from Form instance.
And your have a bad logic for creating form.
The factory method must be return an finished object for next working, but you want expected factory.
Factory for your example:
public function createForm()
{
$form = $this->formFactory->createForm('....');
return $form;
}
Well, I finally resolved the problem creating the form this via:
$myForm = $this->container->get('form.factory')->create(new myEntityType(), new myEntity());
Thanks for your help!