unable to get route name from url in zend framework 2 - zend-framework2

I'm using Zend Framework 2, this is my code:
$prevsingletrackurl = $this->getRequest()->getHeader('Referer')->getUri();
From this code which is defined in a controller's method I'm getting previous URL, now what I need to know some information of this from this, it contains which route, controller, method for my own requirement.

You can use the route stack itself to try to match it. It will return you a RouteMatch if the request object matched:
use Zend\Mvc\Router\RouteMatch;
$referer = $this->getRequest()->getHeader('Referer')->getUri();
$request = clone $this->getRequest();
$request->setUri($referer);
$match = $routeStack->match($request);
if ($match instanceof RouteMatch) {
$route = $match->getMatchedRouteName();
}
You can access the route stack ("router") from the service locator; in the root service locator, it is registered as 'Router'. You can inject the route stack in your factory. For instance in a Controller:
use MyModule\Controller\MyController;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class MyControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $sl)
{
$router = $sl->getServiceLocator()->get('Router');
$controller = new MyController($router);
return $controller;
}
}
Use it in your controller as this:
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Mvc\Router\Http\TreeRouteStack;
class MyController extends AbstractActionController
{
protected $router;
public function __construct(TreeRouteStack $router)
{
$this->router = $router;
}
protected function getRouter()
{
return $this->router;
}
}

This code i have used inside a controller action method
$request = $this->getRequest();
$getHeaderReferer=$request->getHeader('Referer');
if (!empty($getHeaderReferer))
{
$prevsingletrackurl = $getHeaderReferer->getUri(); // get previous url
//echo "<br>previous usrl=>".$prevsingletrackurl;
$controller = $this->getEvent()->getRouteMatch()->getParam('controller'); // get controller name
//**** code to get previous url route , controller name , action/method name starts
$request->setUri($prevsingletrackurl);
$router =$this->getServiceLocator()->get('Router');// this gives instance of /Zend/Mv/Router/Http/TreeRouteStack
//echo "<br>router=>".$router->toString();
$routeMatch = $router->match($request); // this gives instance of /Zend/Mv/Router/Http/RouteMatch
$routename="";
if( $routeMatch instanceof RouteMatch )
{
$urlwholeroutedataAr=$routeMatch->getParams();
if(!empty($urlwholeroutedataAr))
{
$controllerdata=$urlwholeroutedataAr['controller'];
$actionmethod=$urlwholeroutedataAr['action'];
if(array_key_exists('trackdata',$urlwholeroutedataAr))
{
$trackdata=$urlwholeroutedataAr['trackdata'];
}
$controllerdataAr=explode("\\",$controllerdata);
if(count($controllerdataAr)>0)
{
$controllername=end($controllerdataAr);
}
}
$routename=$routeMatch->getMatchedRouteName();
}
}

Related

Is it possible to set translation default domain on a specified path?

My site is in 2 parts, public side with differents locales is working, admin side is translated in only 1 language.
The problem is that the lang file for public side is the same as admin side. I would like to use a different file for all admin routes
I tried to change the domain with {% trans_default_domain "admin" %} but i need to put this line in all my twig files. I didn't find any solution to change the domain in the controller or elsewhere.
I tried also to use a specific locale for admin but the translations are not found and 'admin' is clearly not a language
Thanks for your help :)
I found an half-solution by replacing the Translator class, this post helps me Replacing the Translator service in Symfony 3
This solution change de translation domain for all the methods in the controller and the templates called in it (even the extended template and the includes)
My translations file is 'admin.fr.yml'
I added in Service folder my new Translator class
<?php
namespace App\Service;
use Symfony\Component\Translation\TranslatorBagInterface;
use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\Translation\Translator as BaseTranslator;
class Translator implements LegacyTranslatorInterface, TranslatorInterface, TranslatorBagInterface
{
private $defaultDomain = 'messages';
private $translator;
public function __construct(BaseTranslator $translator)
{
$this->translator = $translator;
}
public function getDefaultDomain(): string
{
return $this->defaultDomain;
}
public function setDefaultDomain(string $defaultDomain): Translator
{
$this->defaultDomain = $defaultDomain;
return $this;
}
public function trans($id, array $parameters = [], $domain = null, $locale = null)
{
if (null === $domain) {
$domain = $this->defaultDomain;
}
return $this->translator->trans($id, $parameters, $domain, $locale);
}
public function getCatalogue($locale = null)
{
return $this->translator->getCatalogue($locale);
}
public function transChoice($id, $number, array $parameters = [], $domain = null, $locale = null)
{
return $this->translator->transChoice($id, $number, $parameters, $domain, $locale);
}
public function setLocale($locale)
{
return $this->translator->setLocale($locale);
}
public function getLocale()
{
return $this->translator->getLocale();
}
}
Then in services.yaml
App\Service\Translator:
decorates: translator
Then in my admin controller folder i created an abstract class BaseAdminController
<?php
namespace App\Controller\Admin;
use App\Service\Translator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Contracts\Translation\TranslatorInterface;
abstract class BaseAdminController extends AbstractController
{
/**
* #var Translator
*/
protected $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
$this->translator->setDefaultDomain('admin');
}
}
Now in each controller where i want admin translations, i just need to replace "extends AbstractController" with "extends BaseAdminController"
If i want to define a construct, i call the parent in it
public function __construct(Translator $translator, EntityManagerInterface $manager)
{
$this->manager = $manager;
parent::__construct($translator);
}

zf2 factories for populating controller with domain objects

I have two different ways of loading my controller with it's domain model. I'd be interested in hearing which is better.
First method - traditional.
A controller factory injects the required service into the controller constructor. Within the controller action, the model is loaded based on the request param:
ClientAppointmentsControllerFactory.php
class ClientAppointmentsControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator) {
$serviceManager = $serviceLocator->getServiceLocator();
$controller = new ClientAppointmentsController($serviceManager->get('Service\ClientAppointments'));
return $controller;
}
}
ClientAppointmentsController.php
class ClientAppointmentsController extends AbstractActionController
{
public function __construct(AppointmentFactory $appointmentFactory){
$this->appointmentFactory = $appointmentFactory;
}
public function indexAction() {
$viewModel = $this->acceptableViewModelSelector($this->acceptCriteria);
$appointments = $this->appointmentFactory->getClientAppointments($this->params()->fromRoute('clientId'));
$viewModel->setVariables([
'appointments' => $appointments
]);
return $viewModel;
}
}
Second Method - Accessing request/route parameters in factory
This seems a bit cleaner to me, as now the controller has no dependency on the service layer, and just expects (from whatever source) an array of loaded objects to pass to the view. I think this still fits the definition of a factory, since it is creating the controller with it's required dependencies, although is now actively creating them instead of passing this onto the controller to do:
ClientAppointmentsControllerFactory.php
class ClientAppointmentsControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator) {
$getRequestParam = function($param) use($serviceLocator){
$serviceManager = $serviceLocator->getServiceLocator();
$request = $serviceManager->get('Request');
$router = $serviceManager->get('Router');
$match = $router->match($request); // \Zend\Mvc\Router\RouteMatch
$result = $match->getParam($param);
return $result;
};
$serviceManager = $serviceLocator->getServiceLocator();
$clientService = $serviceManager->get('Service\ClientAppointments');
$appointments = $clientService->fetchByClientId($getRequestParam('clientId));
$controller = new ClientAppointmentsController($appointments);
return $controller;
}
}
ClientAppointmentsController.php
class ClientAppointmentsController extends AbstractActionController
{
/**
* #param Array $appointments Array of Appointment objects
*/
public function __construct(Array $appointments){
$this->appointments = $appointments
}
public function indexAction() {
$viewModel = $this->acceptableViewModelSelector($this->acceptCriteria);
$viewModel->setVariables([
'appointments' => $this->appointments
]);
return $viewModel;
}
Which is better?
(I also have an idea of a mutable factory floating around.)
IMO, the second is not good at all, because it mixes creation logic with business logic. This means a business logic error will preclude a factory from working.
The first one is better, but not good, because you have got now business logic in the controller.
I would suggest moving business logic into a business model OR to a controller plugin.

How to use dynamic Type Hints in Laravel method

Basically I want to make code generic and use different Services upon given Route parameters.
What is the proper and working way to achieve this?
The following works:
Routes:
Route::get('socialmediaAccount/authorize/twitter', function(TwitterApi $client){ return ['Works'];});
That works as well:
Routes
Route::get('socialmediaAccount/authorize/twitter', ['uses' => 'SocialmediaController#authorizeAccount']);
Controller
class SocialmediaController extends Controller
{
public function authorizeAccount(TwitterApi $client)
{
return ['Works as well'];
}
}
Now I want to add facebook, the idea is this:
Routes
Route::get('socialmediaAccount/authorize/{type}', ['uses' => 'SocialmediaController#authorizeAccount']);
Controller
class SocialmediaController extends Controller
{
public function authorizeAccount($type)
{
if($type == 'twitter') {
$client->call TwitterApi-method(); //????????????
return ['???'];
}
if($type == 'facebook') {
$client->call FacebookApi-method(); //????????????
return ['???'];
}
}
}
Since this didn't work I tried and failed with following:
Now in the definition of my Controller method I can't use the Type Hint anymore, and if I try to create separate methods ala authorizeTwitter I can't call it with Type Hint. I tried following:
Routes - is the same
Controller
class SocialmediaController extends Controller
{
public function authorizeAccount($type)
{
if($type == 'twitter') {
$this->authorizeTwitter();
return ['???'];
}
if($type == 'facebook') {
$this->authorizeFacebook();
return ['???'];
}
}
private function authorizeTwitter(TwitterApi $client) //????????????
{
call TwitterApi-method();
}
private function authorizeFacebook(FacebookApi $client) //????????????
{
call TwitterApi-method();
}
}
The error here is 'Argument 1 passed to ... must be an instance of ... TwitterApi, none given.
You can't use dependency injection on methods that you call manually. Only on route methods, because route methods will be resolved automatically.
This said the following should work:
class SocialmediaController extends Controller
{
public function authorizeAccount($type, TwitterApi $twitterClient, FacebookApi $facebookClient)
{
if($type == 'twitter') {
// do Twitter authorization using $twitterClient here
}
if($type == 'facebook') {
// do Facebook authorization using $facebookClient here
}
}
}

getServiceLocator() in Service Layer in ZF2?

I created a service layer AbcService in order to allow modules to access common lines of code. But I need to use database to extract values in my AbcService. So, I need to call getAbcTable() which calls $service->getServiceLocator(). When I try this, I get an error saying 'Call to undefined method getServiceLocator().
public function getAbcTable()
{
if (!$this->abcTable) {
$sm = $this->getServiceLocator();
$this->abcTable = $sm->get('Abc\Model\AbcTable');
}
return $this->abcTable;
}
You're trying to call a method that presumably doesn't exist. If you need AbcTable in your service, you should pass it in as a dependency.
Create a factory for your service that does this, in Module.php:
public function getServiceConfig()
{
return array(
'factories' => array(
'AbcService' => function($sm) {
$abcTable = $sm->get('Abc\Model\AbcTable');
$abcService = new AbcService($abcTable);
return $abcService;
},
);
}
and modify the constructor for your service to accept the table as a paramter:
class AbcService
{
protected $abcTable;
public function __construct($abcTable)
{
$this->abcTable = $abcTable;
}
// etc.
}
then, wherever you need AbcService, either inject it in, or grab it from the service locator:
public function indexAction()
{
$abcService = $this->getServiceLocator()->get('AbcService');
}
and the service will have the table class in it.

Url.RouteUrl not gettting an API route in MVC5

I created a MVC 5 site using its template and added the following API controller:
namespace MvcSite.Controllers {
public class TestController : ApiController {
[Route("test/send"), HttpGet]
public String Send() {
return "SENT";
}
} // TestController
} // MvcSite.Controllers
When I access "/test/send" I get the string "SENT" back as expected ...
In a Razor view or in a Controller I need to get the URL of the send action so I tried:
var url = Url.RouteUrl(new { controller = "Test", action = "Send", HttpRoute = true });
But url is null ... I have no idea why ...
The API route is working fine so it is in the Route Table ...
What am I missing?
Thank You,
Miguel
Configure a named route and use the HttpRouteUrl method to get the URL.
[Route("test/send", Name = "Send"), HttpGet]
public String Send() {
return "SENT";
}
Get the URL like this:
var url = Url.HttpRouteUrl("Send", new {});

Resources