How to use third party api in symfony2 - symfony

basically I am trying to integrate the thrid party api in symfony2 application. I have the php class from api documention. I want integrate that in my custom bundle controller.
I also looking to add the option to configure the api key and secrets. I don't know how to start. Please look at below that i want to do
Example:-
namespace Acme\DemoBundle\Controller;
class DemoController extends Controller
{
public function indexAction()
{
$myApi = new MyApi($key,$secret); // this is the stuff, I am trying to do!
return array();
}
}
First I try to create the library format symfony package. I don't know how to achive this this type. Can Some one tell me what are thinks I need to do.

TO do this in a proper way, you have to declare you api class as a service & use dependancy injection to inject your parameters :
parameters:
your_api.class: Acme\HelloBundle\Lib\YourApiClass
your_api.key: "key value for your api"
your_api.token: "token value for your api"
services:
yourApiService:
class: "%your_api.class%"
arguments: ["%your_api.key%", "%your_api.token%"]
And in your controller & many other places you'll have access like that :
$api = $this->get('yourApiService');
Take a look for more informations : http://symfony.com/doc/current/book/service_container.html

you can use it as a service:
http://symfony.com/doc/current/book/service_container.html
when i try to implement an extern api, i think about adapter pattern:
http://en.wikipedia.org/wiki/Adapter_pattern

Related

How to extend FOSRestBundle RequestBodyParamConverter?

I am new to Symfony (5.3) and would like to extend the RequestBodyParamConverter (FOSRestBundle 3.0.5) to create a REST api. Using #ParamConverter annotation with the RequestBodyParamConverter works fine. However, I would like to create a custom converter, which does the exact same job as RequestBodyParamConverter plus a little extra work.
My first guess was to simply extend RequestBodyParamConverter and provide my custom subclass in the #ParamConverter annotation. However, RequestBodyParamConverter is defined as final and thus cannot be extended...
Injecting RequestBodyParamConverter / fos_rest.request_body_converter into a custom converter class (see example below) also fails because the service cannot be found. I assume this is because it is defined a private?
So, my last idea was to create a RequestBodyParamConverter inside my custom converter class. While this works, I am not sure if this is the right way to solve this problem. This way RequestBodyParamConverter is created twice. This is nothing special of course, but is this the Symfony way to solve this or are there other solutions?
Example:
Inject RequestBodyParamConverter in custom converter class
class MyParamConverter implements ParamConverterInterface {
protected $parentConverter;
public function __construct(ParamConverterInterface $parentConverter) {
$this->parentConverter = $parentConverter;
}
public function apply(Request $request, ParamConverter $configuration): bool {
doExtraWork();
return $this->parentConverter->apply(...);
}
}
// config/services.yaml
My\Project\MyParamConverter:
tags:
- { name: request.param_converter, converter: my_converter.request_body }
arguments:
# both fails since service is not found
$parentConverter: '#FOS\RestBundle\Request\RequestBodyParamConverter'
# OR
$parentConverter: '#fos_rest.request_body_converter'
Create RequestBodyParamConverter in custom converter class
class MyParamConverter implements ParamConverterInterface {
protected $parentConverter;
public function __construct(...parameters necessary to create converter...) {
$this->parentConverter = new RequestBodyParamConverter(...);
}
...
}
Symfony provide a way to decorate a registered service
To use it you need the FOS service id registered in the container.
To get it you can use this command
symfony console debug:container --tag=request.param_converter
Retrieve the Service ID of the service you want to override.
Then you can configure your service to decorate FOS one
My\Project\MyParamConverter:
decorates: 'TheIdOf_FOS_ParamConverterService'
arguments: [ '#My\Project\MyParamConverter.inner' ] # <-- this is the instance of fos service
Maybe you'll need to add the tags to this declaration, I'm not sure.
Let me know if you're facing an error.

symfony 3 tagging services for using in autowiring service

I tried to create an interface to create tagged services that can be injected into another service based on the documentation here https://symfony.com/doc/3.4/service_container/tags.html
I created an interface like
namespace AppBundle\Model;
interface PurgeInterface {
//put your code here
public function purge ();
}
put the definition into the service.yml:
_instanceof:
AppBundle\Model\PurgeInterface:
tags: ['app.purge']
and create services on this interface.
console debug:container shows my services as properly tagged.
I created another service which should work with the tagged services but this do not work.
services.yml:
purge_manager:
class: AppBundle\Service\PurgeManager
arguments: [!tagged app.purge]
The service looks like:
namespace AppBundle\Service;
use AppBundle\Model\PurgeInterface;
class PurgeManager {
public function __construct(iterable $purgers) {
dump($purgers);
}
}
If I test this I get:
Type error: Too few arguments to function AppBundle\Service\PurgeManager::__construct(), 0 passed in /.....Controller.php on line 21 and exactly 1 expected
I havenĀ“t tried to create a compiler pass because I just want to understand why this is not working as it should based on the documentation
Thanks in advance
Sebastian
You can use tags, manual service definition and _instanceof in config. It's one of Symfony ways, but it requires a lot of YAML coding. What are other options?
Use Autowired Array
I've answered it here, but you use case much shorter and I'd like to answer with your specific code.
The simplest approach is to autowire arguments by autowired array.
no tag
support PSR-4 autodiscovery
no coding outside the service
1 compiler pass
Example
namespace AppBundle\Service;
use AppBundle\Model\PurgeInterface;
class PurgeManager
{
/**
* #param PurgeInterface[] $purgers
*/
public function __construct(iterable $purgers) {
dump($purgers);
}
}
This is also called collector pattern.
How to Integrate
Read a post with an example about this here
Or use the Compiler pass
If there are some incompatible classes, exclude them in the constructor of compiler pass:
$this->addCompilerPass(new AutowireArrayParameterCompilerPass([
'Sonata\CoreBundle\Model\Adapter\AdapterInterface'
]);

Getting a list of tagged services in my controller

What i want is to add services to the service container that i want to use later in my controller or service.
So i created two services with my custom tag fbeen.admin
here they are:
services:
app.test:
class: AppBundle\Admin\TestAdmin
tags:
- { name: fbeen.admin }
fbeen.admin.test:
class: Fbeen\AdminBundle\Admin\TestAdmin
tags:
- { name: fbeen.admin }
Now i want to use all the services with the tag fbeen.admin in my controller but i dont know how.
I followed the How to work with service tags tutorial but i get stuck on this rule:
$definition->addMethodCall('addTransport', array(new Reference($id)));
On some way the addTransport method of the TransportChain class should be called but it seems that it isn't been called.
And even if it would be called then i still do not have a list of services with the fbeen.admin tag into my controller.
I am sure that i am missing something but who can explain me what it is?
p.s. I know that compilerPass runs at buildtime but for example sonata admin knows all admin classes and twig knows all twig extensions. How do they know?
Thank you for reading this :-)
Symfony 3.3
Container gets compiled once (in debug more often, but in production only once). What you manage with addMethodCall... is that once you request your service from container, which you are storing in $definition (that in this case is controller). Then container will call method addMethodCall('method'.. during initialising your service.
What it will look in container:
// This is pseudo content of compiled container
$service = new MyController();
// This is what compiler pass addMethodCall will add, now its your
// responsibility to implement method addAdmin to store admins in for
// example class variable. This is as well way which sonata is using
$service->addAdmin(new AppBundle\Admin\TestAdmin());
$service->addAdmin(new AppBundle\Admin\TestAdmin());
return $service; // So you get fully initialized service
Symfony 3.4+
What you can do is this:
// Your services.yaml
services:
App/MyController/WantToInjectSerivcesController:
arguments:
$admins: !tagged fbeen.admin
// Your controller
class WantToInjectSerivcesController {
public function __construct(iterable $admins) {
foreach ($admins as $admin) {
// you hot your services here
}
}
}
Bonus autotagging of your services. Lets say all your controllers implements interface AdminInterface.
// In your extension where you building container or your kernel build method
$container->registerForAutoconfiguration(AdminInterface::class)->addTag('fbeen.admin');
This will tag automatically all services which implement your interface with tag. So you don't need to set tag explicitly.
The thing to note here is this: The CompilerPass doesn't run the 'addTransport' (or whatever you may call it) in the compiler-pass itself - just says 'when the time is right - run $definition->addTransport(...) class, with this data'. The place to look for where that happens is in your cache directory (grep -R TransportChain var/cache/), where it sets up the $transportChain->addTransport(...).
When you come to use that service for the first time - only then is the data filled in as the class is being instantiated from the container.
This worked for me:
extend the TransportChain class with a getTransports method:
public function getTransports()
{
return $this->transports;
}
and use the TransportChain service in my controller:
use AppBundle\Mail\TransportChain;
$transportChain = $this->get(TransportChain::class);
$transports = $transportChain->getTransports();
// $transports is now an array with all the tagged services
Thank you Alister Bulman for pushing me forwards :-)

Symfony2 Adding 3rd party Bundles to a Service Controller

I am looking to get some help on how to add a 3rd party bundle to an existing service controller, specifically KnpSnappyBundle https://github.com/KnpLabs/KnpSnappyBundle.
The bundle was installed with composer, and registerBundles() function in app/AppKernel.php was updated.
I am having trouble with passing the bundle to a service controller, via its constructor.
1: Unsure how to figure out the path to use
--SomeController.php file--
...
use Symfony\Component\???\???
class FormDataController
{
...
private $pdf;
2: Also unsure how to figure out what the object type is named.
public function __construct(..., KnpSnappyBundle? $pdf )
{
...
$this->pdf= $pdf;
return $this;
}
3: Then in the servives.yml file i add the following argument to the correct controller
- "#knp_snappy.pdf"
After doing the above, the controller should be able to do the following to access the bundles functions, correct?
$this->pdf->...
Why are you doing this?
Quote:
The bundle registers two services:
the knp_snappy.image service allows you to generate images;
the knp_snappy.pdf service allows you to generate pdf files.
Just access them like this (if you use standard Symfony controller):
$this->get('knp_snappy.pdf')->...;
$this->get('knp_snappy.image')->...;
UPDATE:
If you are injecting knp_snappy.pdf service into your custom service, you should use Knp\Bundle\SnappyBundle\Snappy\LoggableGenerator class.
--SomeController.php file--
...
use Knp\Bundle\SnappyBundle\Snappy\LoggableGenerator;
class FormDataController
{
// ... //
private $pdf;
public function __construct(..., LoggableGenerator $pdf )
{
...
$this->pdf = $pdf;
return $this;
}
// ... //
}
When you run ./app/console container:debug command, it lists all available services and corresponding classes. Very helpful.

How to use a cookie in routing configuration in Symfony2?

I have a City parameter stored in a cookie. I would like to include its value as a pattern prefix in my routing configuration like so:
# MyBundle/Resources/config/routing.yml
MyBundle_hotel:
resource: "#MyBundle/Resources/config/routing/hotel.yml"
prefix: /%cityNameFromCookie%/hotel
How can I achieve that?
Give us a use case on how you would want this to work because I don't see the difficulty. Routes are made of parameters that you can specify through the generateUrl function, the url twig function or the path twig function.
In Twig you can do this
{{ path('MyBundle_hotel', {cityNameFromCookie: app.request.cookies.get('cityNameFromCookie')}) }}
In a controller action
$cookieValue = $this->get('request')->cookies->get('cityNameFromCookie');
$url = $this->generateUrl('MyBundle_hotel', array('cityNameFromCookie' => $cookieValue));
Or from any places that have access to the container
$cookieValue = $this->container->get('request')->cookies->get('cityNameFromCookie');
$url = $this->container->get('router')->generate('MyBundle_hotel', array('cityNameFromCookie' => $cookieValue));
In the last example, you will probably want to change how the container is being accessed.
If you are concerned about how complicated it looks like, you can abstract this logic and put it inside a service or extend the router service.
You can find documentation about services and the service container in the Symfony's documentation.
You can also list the services via the command php app/console container:debug and will find the router service and its namespace and from this you can try to figure out how to extend the router service (a very good way to learn how services work).
Otherwise, here is simple way to create a service.
In your services.yml (either in your Bundle or in app/config/config.yml)
services:
city:
class: MyBundle\Service\CityService
arguments: [#router, #request]
In your CityService class
namespace MyBundle\Service
class CityService
{
protected $router;
protected $request;
public function __construct($router, $request)
{
$this->router = $router;
$this->request = $request;
}
public function generateUrl($routeName, $routeParams, $absoluteUrl)
{
$cookieValue = $this->request->cookies->get('cityNameFromCookie');
$routeParams = array_merge($routeParams, array('cityNameFromCookie' => $cookieValue));
return $this->router->generateUrl($routeName, $routeParams, $absoluteUrl);
}
}
Anywhere you have access to the container, you will be able to do the following
$this->container->get('city')->generateUrl('yourroute', $params);
If you still think that it isn't a great solution; you will have to extend the router service (or find a better way to extend the router component to make it behave the way you are expecting it to).
I personally use the method above so I can pass an entity to a path method in Twig. You can find an example in my MainService class and PathExtension Twig class defined in the services.yml.
In Twig, I can do forum_path('routename', ForumEntity) and in a container aware environment I can do $this->container->get('cornichon.forum')->forumPath('routename', ForumEntity).
You should have enough information to make an informed decision

Resources