im trying to make a simple custom repository in order to understand how elastic search repository works. the documentation is pretty straight forward but i still dont understand how it works, im getting this error ´The service definition "fos_elastica.manager" does not exist.´. so far i think my problem is in the controller since i dont understand how to intialize them, also i would like to know if im in the right way in my configuration of the custom repository and the simple query i made.
im getting this error with this configuration whenever i try to make a search,
The service definition "fos_elastica.manager" does not exist.
this is my configuration so far:
//app/config.yml
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
sava:
client: default
types:
blog:
mappings:
id:
type: integer
body : ~
title : ~
tags: ~
persistence:
identifier: id
driver: orm
model: sava\BlogBundle\Entity\TblPost
finder: ~
provider: ~
listener: ~
repository: sava\BlogBundle\SearchRepository\TblPostRepository
this is my controller action:
namespace sava\BlogBundle\Controller;
//custom querys
use FOS\ElasticaBundle\Manager\RepositoryManager;
use FOS\ElasticaBundle\Repository;
//
use Symfony\Component\DependencyInjection\ContainerBuilder;
class TblPostController extends Controller
{
public function getPostAction(Request $request)
{
$container = new ContainerBuilder();
$repositoryManager = $container->get('fos_elastica.manager');
$repository = $repositoryManager->getRepository('BlogBundle:TblPost');
$items2 = $repository->matchExact($categoria,$searchQuery );
return $this->render('savaBlogBundle:TblPost:index.html.twig', array(
'results' => $items2, 'entities' => $items2
));
}
this is my post repository:
<?php
namespace sava\BlogBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\ElasticaBundle\Repository;
class TblPostRepository extends FOS\ElasticaBundle\Repository
{
public function matchExact($campo, $searchQuery) {
//$finder = $this->get('fos_elastica.finder.sava.blog');
$query = new Query();
if($searchQuery=='')
{
$innerQuery = new Query\MatchAll();
}
else{
$innerQuery = new Query\Match();
$innerQuery->setField( $campo , array('query' => $searchQuery));
}
$query->setQuery($innerQuery);
$query->setSize(1000000);
$query->setExplain(true);
return $this->find($query);
}
}
and since im using yml this is my tblpost.orm, i did generate my entities.
whenever i do the get postaction it throws me that it cant find the container, and i dont see an example in how to properly intiaze it, also is this is how you make a custom query?
EDIT 1:
so i changed this:
$container = new ContainerBuilder();
$repositoryManager = $container->get('fos_elastica.manager');
to this:
$elastica = $this->container->get('fos_elastica.manager');// single entry point, no fancy services
$SearchRepository = $elastica->getRepository('savaBlogBundle:TblPostRepository');// single type
and im getting this error:
No search finder configured for sava\BlogBundle\Entity\TblPostRepository
I've just had the same issue, the soultion is that instead of savaBlogBundle:TblPostRepository you should use your entity, for example:
$SearchRepository = $elastica->getRepository('savaBlogBundle:TblPost`)
According your fatal error in title of the issue enter link description here
The main problem why did you get that mistake is that, you assigned the same TblPostRepository in (doctrine config for entity) and in fos_elastica.
Related
Following the Dynamic Router doc, I can:
Create a route linked to my content (Page entity)
Persist it with ORM
Load my controller matching the route
But as my controller should expect the $contentDocument parameter, I have nothing.
Here's how I create my route and my entity:
$page = new Page();
$page->setTitle('My Content')
->setBackground(1)
->setDescription('My description')
->setContent('<h1>Hello</h1>');
$manager->persist($page);
$manager->flush(); // flush to be able to use the generated id
$contentRepository = $this->container->get('cmf_routing.content_repository');
$route = new Route();
$route->setName('my-content');
$route->setStaticPrefix('/my-content');
$route->setDefault(RouteObjectInterface::CONTENT_ID, $contentRepository->getContentId($page));
$route->setContent($page);
$page->addRoute($route); // Create the backlink from content to route
$manager->persist($page);
$manager->flush();
Here's my config section:
cmf_routing:
chain:
routers_by_id:
router.default: 200
cmf_routing.dynamic_router: 100
dynamic:
enabled: true
persistence:
orm:
enabled: true
generic_controller: AppBundle:Default:showPage
templates_by_class:
AppBundle\Entity\Page: AppBundle:Default:index.html.twig
My controller:
public function showPageAction($page)
{
return $this->render('default/index.html.twig');
}
And the error:
Controller "AppBundle\Controller\DefaultController::showPageAction()" requires that you provide a value for the "$page" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.
Dynamic routing puts the content document into the request with the name "contentDocument". You need to explicitly use that name:
public function showPageAction($contentDocument)
{
return $this->render('default/index.html.twig');
}
if you do not need to do anything in your controller, you don't need to specify the generic_controller. template_by_class will make the bundle provided controller render that template with the page instance found in $contentDocument.
I am using the FOSElasticaBundle in Symfony 3.3. I have registered an event listener on the POST_TRANSFORM event that computes and adds a custom property like this:
public function addCustomProperty(TransformEvent $event)
{
$document = $event->getDocument();
$custom = $this->anotherService->calculateCustom($event->getObject());
$document->set('custom', $custom);
}
Now I need to set the analyzer to be used for this property. How can I do that?
I already tried to add the custom field name to the type definition in my fos_elastica config but that causes an exception as the bundle then expects that property on my entity as well.
I finally found out that I could use dynamic_templates to set the desired analyzer like this:
fos_elastica:
indexes:
app:
types:
myentity:
dynamic_templates:
custom_field:
path_match: customfield.*
mapping:
analyzer: myanalyzer
properties:
...
I'm building an symfony2 app that is configurable up to some point based on what domain is used to access the site.
For ease of this question, lets say there is an "Domain" entity in the database containing the hostname and further configuration.
Think about minor template differences, some differences in header/footer. A difference in products being offered.
The routes available would not be different.
There are 2 places where I need this Domain object.
* in a Controller::action
* in a base template (even if the controller didn't need it)
I would not need it somewhere else, if I did, I could simply pass it from the controller.
What would be the best way to get this object without creating too much overhead and not fetching it when we don't actually need it.
Some thoughts I got so far:
* I could override the ControllerResolver and determine the Domain object based on the Request object. Although I don't seem to have access to the ServiceContainer there.
* I could add some method to a BaseController that can retrieve the domain for me when I'm in a Controller:Action.
* For usage in the template I could create a TwigExtension that adds a global variable. But it would need access to the Request object or RequestStack. Also, this would only help me in the template, I might be doing the same thing twice.
Any suggestions what might be a good approach here?
Don't know if this is the best solution, but worked well for me so far.
Since the domain information depends on the request it is NOT a service, so don't try to inject it in services or you'll get a bad headache. The most natural place to set information about the domain is in the request, and allow the controllers to read this information to interact with the services.
So, you can setup a Kernel event listeners which read the information from the database and set a domain Request attribute, like this:
<?php
namespace Acme\SiteBundle\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Doctrine\ORM\EntityRepository;
class DomainSubscriber implements EventSubscriberInterface
{
protected $domainRepository;
public static function getSubscribedEvents()
{
return array(
KernelEvents::REQUEST => 'onKernelRequest'
);
}
public function __construct(EntityRepository $domainRepository)
{
$this->domainRepository = $domainRepository;
}
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
// Console/CLI commands don't have Domain info
if ($request === null)
return;
$domain = $this->domainRepository->find($request->getHost());
if ($domain === null)
throw new \RuntimeException(sprintf("Cannot find domain %s", $request->getHost()));
$request->attributes->set('domain', $domain);
}
}
Which must be registered in services.yml (or XML) with:
acme_site.manager:
class: Doctrine\ORM\EntityManager
factory_service: doctrine
factory_method: getManager
acme_site.domain_repository:
class: Doctrine\ORM\EntityRepository
factory_service: acme_site.manager
factory_method: getRepository
arguments:
- 'AcmeSiteBundle:Domain'
acme_site.domain_subscriber:
class: Acme\SiteBundle\EventListener\DomainSubscriber
arguments:
- "#acme_site.domain_repository"
tags:
- { name: kernel.event_subscriber }
In your Controller you can now access the data by simply doing this:
public function someAction(Request $request) {
$domain = $request->attributes->get('domain');
$domain->getWhatever();
}
And in Twig you can always access the request with this:
{% set domain = app.request.attributes.get('domain') %}
whatever: {{ domain.whatever }}
Hope this help!
DISCLAIMER: the code is copy-pasted and then edited, so it may contain some minor error.
NOTE: If you really need to inject the request in services, then I suggest you to read the docs about the RequestStack (Symfony 2.4+), or use a setRequest method and take care of container scopes.
Disclaimer: I'm slowly starting to get into Symfony and still have some problems understanding how the architecture works.
Currently I set up different Bundles (Services, right?) that should deliver different output for different routes. So far I got around adding a simple Twig template that loads stylesheets and scripts via Assetics and Twig-blocks. Now I added another Bundle that queries data via Buzz from a remote location, which worked fine as a standalone script, but I don't get around printing output in a Twig template.
The architecture of the original script is like the following (names made more generic):
Vendors - abstract class that serves as base for all remote request Bundles.
ServiceABC - abstract class that extends Vendors and defines Error handling and output preparation for the ABC service.
ClientXYZ - final class that extends Service_ABC, defines output parsing and normalization of the returned data.
This Bundle got a services.yml file:
# ~/MyApp/Bundle/ServiceABCBundle/Resources/config/services.yml
parameters:
service_abc_manager.class: MyApp\Bundle\ServiceABCBundle\Models\Service_ABC
location_manager.class: MyApp\Bundle\ServiceABCBundle\Models\Clients\ClientLocation
monitor_manager.class: MyApp\Bundle\ServiceABCBundle\Models\Clients\ClientMonitor
services:
service_abc_manager:
abstract: true
location_manager:
class: %location_manager.class%
parent: service_abc_manager
monitor_manager:
class: %monitor_manager.class%
parent: service_abc_manager
Names changed for easier reference - Typos by accident possible.
Now my problem/question is, that I don't really get behind the Symfony2 concept of how to get the output into the template.
namespace MyApp\Bundle\ServiceABCBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use MyApp\Bundle\ServiceABCBundle\Models\Clients\ClientLocation;
class DefaultController extends Controller
{
public function indexAction()
{
$services = array();
$services[] = $this->container->has('service_abc_manager');
$services[] = $this->container->has('location_manager');
$services[] = $this->container->has('client_location');
$services[] = $this->container->has('ClientLocation');
var_dump( $services );
$client = new ClientLocation();
var_dump( $client );
$response = $this->render(
'Service_ABC:Default:index.html.twig'
);
# $response->setCharset( 'utf-8' );
# $response->headers->set( 'Content-Type', 'text/html' );
return $response;
}
}
The output of the first array() named $services is always false and the $client = new ClientLocation(); throws an Exception that the class name wasn't found.
How can I access those Services/Bundle(parts)/Classes? And how would I render the output to a template?
Update
After I added the complete tree definition to Configuration()->getConfigTreeBuilder(), I'm able to see the definitions in the CLI:
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root( 'myapp_service_abc' );
$rootNode
->children()
->scalarNode('service_abc_manager')->end()
->scalarNode('location_manager')->end()
->scalarNode('monitor_manager')->end()
->end()
;
return $treeBuilder;
}
}
The CLI command php app/console config:dump-reference myapp_service_abc now gives me the following output:
myapp_service_abc:
service_abc_manager: ~
location_manager: ~
monitor_manager: ~
I can as well see that the config data was loaded, when I var_dump( $loader ); inside MyAppServiceABCExtension right after $loader->load( 'services.yml' ); was called.
The output is the following:
object(Symfony\Component\DependencyInjection\Loader\YamlFileLoader)
protected 'container' =>
object(Symfony\Component\DependencyInjection\ContainerBuilder)
private 'definitions' =>
array
'service_abc_manager' =>
object(Symfony\Component\DependencyInjection\Definition)
'location_manager' =>
object(Symfony\Component\DependencyInjection\DefinitionDecorator)
private 'parent' => string 'service_abc_manager'
// etc.
The problem itself remains: There's still a FALSE return value inside DefaultController()->indexAction() when I var_dump( $this->container->has( 'service_abc_manager' );. I as well tried var_dump( $this->container->has( 'location_manager' ); and var_dump( $this->container->has( 'myapp.service_abc_manager' ); with the same result.
You should not call your services from the twig file, but from the controller.
The role of the controller is to :
validate your forms if there were a form posted
call your services to get some stuffs to display in a view
initialize forms if there is a form to display
return a Response that typically contains a rendered twig view
Do not call your services using something like $client = new ClientLocation();, but call it using the service container. This will allow you to take the whole power of the dependancy injection offered by Symfony2.
Your controller will look like :
<?php
namespace MyApp\Bundle\ServiceABCBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller
{
public function indexAction()
{
$locationService = $this->container->get('location_manager');
$someStuffs = $locationService->someMethod();
$response = $this->render(
'ServiceABCBundle:Default:index.html.twig', array('stuffs' => $someStuffs)
);
return $response;
}
}
From your twig file, you'll be able to use the stuffs variable :
{{ stuffs }} if your variable is a terminal ( a string, a number... )
{{ stuffs.attribute }} if your variable is an object or an array
About your services file, I am a bit confused, because your architecture does not look to be the standard Symfony2's one :
# ~/MyApp/Bundle/ServiceABCBundle/Resources/config/services.yml
Why your services.yml file isn't in the src/MyApp/SomethingBundle/Resources/config/ directory?
If you didn't already read it, I suggest you to have a look to the Symfony2 : The Big Picture documentation, which is the best way to start with Symfony2.
I have added a setting to my config.yml file as such:
app.config:
contact_email: somebody#gmail.com
...
For the life of me, I can't figure out how to read it into a variable. I tried something like this in one of my controllers:
$recipient =
$this->container->getParameter('contact_email');
But I get an error saying:
The parameter "contact_email" must be
defined.
I've cleared my cache, I also looked everywhere on the Symfony2 reloaded site documentation, but I can't find out how to do this.
Probably just too tired to figure this out now. Can anyone help with this?
Rather than defining contact_email within app.config, define it in a parameters entry:
parameters:
contact_email: somebody#gmail.com
You should find the call you are making within your controller now works.
While the solution of moving the contact_email to parameters.yml is easy, as proposed in other answers, that can easily clutter your parameters file if you deal with many bundles or if you deal with nested blocks of configuration.
First, I'll answer strictly the question.
Later, I'll give an approach for getting those configs from services without ever passing via a common space as parameters.
FIRST APPROACH: Separated config block, getting it as a parameter
With an extension (more on extensions here) you can keep this easily "separated" into different blocks in the config.yml and then inject that as a parameter gettable from the controller.
Inside your Extension class inside the DependencyInjection directory write this:
class MyNiceProjectExtension extends Extension
{
public function load( array $configs, ContainerBuilder $container )
{
// The next 2 lines are pretty common to all Extension templates.
$configuration = new Configuration();
$processedConfig = $this->processConfiguration( $configuration, $configs );
// This is the KEY TO YOUR ANSWER
$container->setParameter( 'my_nice_project.contact_email', $processedConfig[ 'contact_email' ] );
// Other stuff like loading services.yml
}
Then in your config.yml, config_dev.yml and so you can set
my_nice_project:
contact_email: someone#example.com
To be able to process that config.yml inside your MyNiceBundleExtension you'll also need a Configuration class in the same namespace:
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root( 'my_nice_project' );
$rootNode->children()->scalarNode( 'contact_email' )->end();
return $treeBuilder;
}
}
Then you can get the config from your controller, as you desired in your original question, but keeping the parameters.yml clean, and setting it in the config.yml in separated sections:
$recipient = $this->container->getParameter( 'my_nice_project.contact_email' );
SECOND APPROACH: Separated config block, injecting the config into a service
For readers looking for something similar but for getting the config from a service, there is even a nicer way that never clutters the "paramaters" common space and does even not need the container to be passed to the service (passing the whole container is practice to avoid).
This trick above still "injects" into the parameters space your config.
Nevertheless, after loading your definition of the service, you could add a method-call like for example setConfig() that injects that block only to the service.
For example, in the Extension class:
class MyNiceProjectExtension extends Extension
{
public function load( array $configs, ContainerBuilder $container )
{
$configuration = new Configuration();
$processedConfig = $this->processConfiguration( $configuration, $configs );
// Do not add a paramater now, just continue reading the services.
$loader = new YamlFileLoader( $container, new FileLocator( __DIR__ . '/../Resources/config' ) );
$loader->load( 'services.yml' );
// Once the services definition are read, get your service and add a method call to setConfig()
$sillyServiceDefintion = $container->getDefinition( 'my.niceproject.sillymanager' );
$sillyServiceDefintion->addMethodCall( 'setConfig', array( $processedConfig[ 'contact_email' ] ) );
}
}
Then in your services.yml you define your service as usual, without any absolute change:
services:
my.niceproject.sillymanager:
class: My\NiceProjectBundle\Model\SillyManager
arguments: []
And then in your SillyManager class, just add the method:
class SillyManager
{
private $contact_email;
public function setConfig( $newConfigContactEmail )
{
$this->contact_email = $newConfigContactEmail;
}
}
Note that this also works for arrays instead of scalar values! Imagine that you configure a rabbit queue and need host, user and password:
my_nice_project:
amqp:
host: 192.168.33.55
user: guest
password: guest
Of course you need to change your Tree, but then you can do:
$sillyServiceDefintion->addMethodCall( 'setConfig', array( $processedConfig[ 'amqp' ] ) );
and then in the service do:
class SillyManager
{
private $host;
private $user;
private $password;
public function setConfig( $config )
{
$this->host = $config[ 'host' ];
$this->user = $config[ 'user' ];
$this->password = $config[ 'password' ];
}
}
I have to add to the answer of douglas, you can access the global config, but symfony translates some parameters, for example:
# config.yml
...
framework:
session:
domain: 'localhost'
...
are
$this->container->parameters['session.storage.options']['domain'];
You can use var_dump to search an specified key or value.
In order to be able to expose some configuration parameters for your bundle you should consult the documentation for doing so. It's fairly easy to do :)
Here's the link: How to expose a Semantic Configuration for a Bundle
Like it was saying previously - you can access any parameters by using injection container and use its parameter property.
"Symfony - Working with Container Service Definitions" is a good article about it.
I learnt a easy way from code example of http://tutorial.symblog.co.uk/
1) notice the ZendeskBlueFormBundle and file location
# myproject/app/config/config.yml
imports:
- { resource: parameters.yml }
- { resource: security.yml }
- { resource: #ZendeskBlueFormBundle/Resources/config/config.yml }
framework:
2) notice Zendesk_BlueForm.emails.contact_email and file location
# myproject/src/Zendesk/BlueFormBundle/Resources/config/config.yml
parameters:
# Zendesk contact email address
Zendesk_BlueForm.emails.contact_email: dunnleaddress#gmail.com
3) notice how i get it in $client and file location of controller
# myproject/src/Zendesk/BlueFormBundle/Controller/PageController.php
public function blueFormAction($name, $arg1, $arg2, $arg3, Request $request)
{
$client = new ZendeskAPI($this->container->getParameter("Zendesk_BlueForm.emails.contact_email"));
...
}
Inside a controller:
$this->container->getParameter('configname')
to get the config from config/config.yaml:
parameters:
configname: configvalue