How can I route to a module? - php

I have poured over the documentation but I can't seem to find out how I can route a URI to a module.
My module currently contains a single controller using the correct directory structure (currently a ton of empty directories). I have my controller inside modules/module_name/classes/controller and my routes file inside modules/module_name/config/routes.php.
I have tried the following in both /app/config/routes.php and modules/module_name/config/routes.php:
<?php
return array(
'_root_' => 'md5_encrypt/index', // The default route
'tools/geek/md5_encrypt' => array('md5_encrypt'),
);
The controller looks like below (but I don't think that is relevant):
<?php
/**
* MD5 Encrypt Controller.
*
* Online tool to encrypt a string using MD5
*
* #package app
* #extends Controller
*/
namespace Md5_encrypt;
class Controller_Md5_Encrypt extends Controller_Template
{
/**
* The tool
*
* #access public
* #return Response
*/
public function action_index()
{
$data = array();
$this->template->tab = 'geek';
$this->template->title = 'MD5 Encrypt Tool';
$this->template->content = View::forge('welcome/index', $data);
}
}

You can't have an underscore in a namespace name. Same for the controller name.
The autoloader will convert underscores to directory separators when looking for the file.

First you should set the path of your application modules in app/config/config.php
'module_paths' => array(
APPPATH.'modules'.DS, // path to application modules
)
Second set routing in app/config/routes.php
'tools/geek/md5_encrypt' => 'md5_encrypt(module_name)/md5_encrypt(controller)',
However, since you are using underscore for your Controller's Name class Controller_Md5_Encrypt extends Controller_Template, it resulted to a new path.
/modules/md5_encrypt/classes/controller/md5/encrypt.php
Underscore(_) in your Controller's name was converted to a directory separator during autoloading http://fuelphp.com/docs/general/coding_standards.html#classes
Your /modules/md5_encrypt/classes/controller/md5_encrypt.php file was not found during autoloading.

Related

Symfony 4 multilang routes with default fallback?

I'm trying to utilize Symfony 4's multilang (or multi-locale) routing pattern.
My application is truly international and supports over 25 different languages, though the translations come incrementally and many routes are not translated to some languages yet.
In such case I want them to fall back into english-default.
My config/packages/translation.yaml looks like this:
framework:
default_locale: en
translator:
default_path: '%kernel.project_dir%/translations'
fallbacks:
- en
My routes are defined within routes.yaml file. For example:
about_index:
path:
en: /about-us
pl: /o-nas
controller: Symfony\Bundle\FrameworkBundle\Controller\TemplateController
defaults:
template: About/index.html.twig
Now, whenever I open the site with either pl or en locale - everything works as expected, but when for example I set it to de, I get "Unable to generate a URL for the named route "about_index" as such route does not exist." error.
How do I force Symfony to fallback to en paths whenever the route in desired locale does not yet exist?
So, after quite a bit of investigation it appears there's no way to make it work like that with Symfony's default methods.
I went for the "workaround" approach and extended Symfony's Twig Bridge's Routing extension with my own Twig function, autopath():
namespace App\Twig;
use Symfony\Bridge\Twig\Extension\RoutingExtension;
use Twig\TwigFunction;
// auto-wired services
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Extends Symfony's Twig Bridge's routing extension providing more flexible localization.
*
* #author Kyeno
*/
class KyAutoPathExtension extends RoutingExtension
{
private $router;
private $request;
public function __construct(UrlGeneratorInterface $router, RequestStack $requestStack)
{
$this->router = $router;
$this->request = $requestStack->getCurrentRequest();
parent::__construct($router);
}
/**
* {#inheritdoc}
*
* #return TwigFunction[]
*/
public function getFunctions()
{
return [
new TwigFunction('autopath', [$this, 'getPathAuto'], ['is_safe_callback' => [$this, 'isUrlGenerationSafe']])
];
}
/**
* #param string $name
* #param array $parameters
* #param bool $relative
*
* #return string
*/
public function getPathAuto($name, $parameters = [], $relative = false)
{
// obtain current and default locales from request object
$localeRequested = $this->request->getLocale();
$localeDefault = $this->request->getDefaultLocale();
// build localized route name
// NOTE: Symfony does NOT RECOMMEND this way in their docs, but it's the fastest that popped in my mind
foreach([sprintf('%s.%s', $name, $localeRequested), sprintf('%s.%s', $name, $localeDefault)] as $nameLocalized) {
// if such route exists, link to it and break the loop
if($this->router->getRouteCollection()->get($nameLocalized)) {
return $this->router->generate($nameLocalized, $parameters, $relative ? UrlGeneratorInterface::RELATIVE_PATH : UrlGeneratorInterface::ABSOLUTE_PATH);
}
}
// when no matches found, attempt relying on Symfony Twig Bridge's original path() function
// (and likely fail with exception, unless they fix/allow it)
return parent::getPath($name, $parameters, $relative);
}
}
Works on SF 4.4, using annotations : you can declare double #Route annotations, one with localized routes, the other without localized. The non-localized route will be used if not matching the first annotation.
* #Route({
* "fr": "/bonjour",
* "de": "/guten-tag"
* }, name="hello_path")
* #Route("/hello", name="hello_path")

Yii2, Component, Make a single instance and access it everywhere in the web application

I would like to create a component in yii2 that can be accessed throughout the web application but only create one instance and be able to retrieve that instance wherever needed.
namespace app\components;
use yii;
use yii\base\Object;
class ContentManagerComponent extends Object
{
public function init(){
parent::init();
}
public function toBeUsed (){
return 'some variable';
}
}
Then I want to be able to call the component in other parts of the web application, like in the controllers.
namespace app\Controllers;
use yii;
use app\controllers\
class SomeController extends Controller {
public function actionDoSomething(){
$contentComponent = Yii::$app->content;
$someVariable = $contentComponent->toBeUsed()
return $this->render( 'someView',[
'variable' => $someVariable,
]
}
}
I have also put the component in the web.php file.
$config = [
'components' => [
'content' => [
'class' => 'app\components\ContentManagerComponent',
],
],
],
What I'm ending up with is phpstorm telling me that the class doesn't exist. I would also like to have intelisense like the other components do in the application.
intelisense:
noIntele:
update:#
I was able to get intelisense working by adding the this line as suggested by the answer below. /** #var ContentComponent $contentManager */
However I got tired of always typing that out above each time I wanted to use the Content Component. So I created a function in the base class of the components I was needing Content Component that return the Continent Component using the Yii::app->content method. Above the function that would return the Content Component I added the comment that it would return ContentComponent and the class of the ContentComponent. Now in order for me to use the component with intelisense working. All I would have to do is $this->getContentComponent. Php storm would be able to identify that the content component was of the class returned. Bellow is an example.
class BaseClass extends object
{
/**
* #return ContentComponent
*/
function getContentComponent () {
$contentComponent = Yii::app->content;
return $contentComponent
}
}
class SomeClass extends BaseClass
public function someFunction () {
$contentComponent = $this->getContentComponent;
}
PHPStorm don't recognize your custom component because they are created dynamically on framework load and attached to Yii::$app on runtime, That's why PHPStorm don't recognize your custom components. So until someone will develop an intelligent plugin for IDEs like PHPStorm, you will have to make some tweaks to achieve your goals.
You have 2 options:
Create a new Yii.php file (in root dir) for reference with all the
necessary docs and this will tell PHPStorm in the entire project
about your components (I putted here a full example, if you want to create components which available only for console/web/both) look at * #property ContentManagerComponent $content (More read - credit to samdark Alexander Makarov, one of Yii core contributors):
<?php
use app\components\ContentManagerComponent;
use yii\BaseYii;
/**
* Class Yii
* Yii bootstrap file.
* Used for enhanced IDE code autocompletion.
*/
class Yii extends BaseYii
{
/**
* #var BaseApplication|WebApplication|ConsoleApplication the application instance
*/
public static $app;
}
/**
* Class BaseApplication
* Used for properties that are identical for both WebApplication and ConsoleApplication
*
* #property ContentManagerComponent $content
*/
abstract class BaseApplication extends yii\base\Application
{
}
/**
* Class WebApplication
* Include only Web application related components here
*
*/
class WebApplication extends yii\web\Application
{
}
/**
* Class ConsoleApplication
* Include only Console application related components here
*/
class ConsoleApplication extends yii\console\Application
{
}
Create a PHP doc everywhere you want to use your component which
will tell PHPStorm that your variable is instance of the component:
public function actionDoSomething()
{
/** #var ContentManagerComponent $contentComponent */
$contentComponent = Yii::$app->content;
$someVariable = $contentComponent->toBeUsed();
return $this->render('someView', [
'variable' => $someVariable,
]);
}
As you can see option 1 is a solution provided by one of the core contributors of the Yii framework, so I assumes that this suppose to be the right choice for now (until there will be native support by JetBrains or any plugin)
Once you have declared your content component in you config files
$config = [
'components' => [
'content' => [
'class' => 'app\components\ContentManagerComponent',
],
],
],
then you can refer tor the component using
Yii::$app->content
eg
Yii::$app->content->yourMethod();
eventually add use Yii; or refer using \Yii::$app->content
I use the following method for intellisense.
1.Set your components inside config.
$config = [
'components' => [
'content' => [
'class' => 'app\components\ContentManagerComponent',
],
'content2' => [
'class' => 'app\components\ContentManagerComponent2',
],
],
],
2.Have an AppComponents trait, documenting all instances that your $app has. I like to have it inside components/ directory.
<?php
namespace app\components;
/**
* Trait AppComponents
* #package app\components
*
* #property ContentManagerComponent1 $content
* #property ContentManagerComponent2 $content2
*/
trait AppComponents {}
3.Return the Yii::$app your own way. Trick the editor into believing that AppComponents may be returned.
<?php
namespace app\controllers;
use frontend\components\AppComponents;
use yii\rest\Controller;
class SiteController extends Controller {
/**
* #return \yii\web\Application|AppComponents
*/
public static function app() {
return \Yii::$app;
}
}
Now you can use SiteController::app()->content with intellisense. You can have a nicer Root class, and replace \Yii::$app with Root::app(). All Controllers may inherit from the Root class. You can also use self::app() when coding inside the extended Controllers.

Zend Framework 2: Getting current controller name inside a module

I'm creating a module X that will be instantiated inside a controller module. How can I get the name of that controller in a method of module X?
This module is not a controller, nor a view or layout.
An example. This is an action in the controller:
public function indexAction {
$parser = new Parser();
}
And this my new module Parser, where I need to know the controller's name.
public function __construct() {
$controller_name = ???
}
For such dependencies you should use a factory to create your service instance. Then you can inject whatever you want in there, also a controller name. Your ParserFactory could for example look like this:
<?php
namespace Application\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Application\Service\Parser
class ParserFactory implements FactoryInterface
{
/**
* #param ServiceLocatorInterface $serviceLocator
* #return Parser
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$routeMatch = $serviceLocator->get('Application')->getMvcEvent()->getRouteMatch();
$controllerName = $routeMatch->getParam('controller');
$parser = new Parser($controllerName);
return $parser;
}
}
Your Parser class:
<?php
namespace Application\Service;
class Parser
{
/**
* #param string $controllerName
*/
public function __construct($controllerName)
{
//... use your controller name ...
}
}
Register your factory in module.config.php like this:
'service_manager' => array(
'factories' => array(
'Parser' => 'Application\Factory\ParserFactory',
)
)
Get your service where you need it from the ServiceManager like this:
$parser = $serviceManager->get('Parser');
I think this has been asked before but I think you do:
$this->getEvent()->getRouteMatch()->getParam('controller', 'index');
You should just be able to grab it all from the router.
EDIT:
Yeah, check these out:
How to get the controller name, action name in Zend Framework 2
ZF2 - Get controller name into layout/views
ZF2: Get module name (or route) in the application layout for menu highlight

Laravel can't instantiate interface via __construct (using App::bind)

I am trying to resolve class via __construct using Laravel's bind() method.
Here what I do:
routes.php (of course I will move it away from here)
// Bindings
App::bind(
'License\Services\ModuleSelector\SelectorInterface',
'License\Services\ModuleSelector\ModuleSelector'
);
SelectorInterface.php - interface that I will expect in __construct method.
<?php namespace License\Services\ModuleSelector;
interface SelectorInterface {
/**
* Simply return query that will select needle module fields
*
* #return mixed
*/
public function make();
}
ModuleSelector.php - this is class that I want to resolve via Laravel's DI (see example below).
<?php namespace License\Services\ModuleSelector;
use License\Services\ModuleSelector\Selector;
class ModuleSelector extends Selector
{
/**
* Get module by it's code
*
* #return mixed
*/
public function find()
{
return $this->make()
->where('code', $module_code)
->first();
}
}
Module.php
<?php namespace License\Services\ModuleType;
use License\Services\ModuleType\TypeInterface;
use License\Services\ModuleSelector\SelectorInterface;
class Module
{
...
function __construct(SelectorInterface $selector)
{
$this->selector = $selector;
}
...
}
And the place when error occurs:
In my repo I have use License\Services\ModuleType\Module as ModuleService;.
Than there is method called find():
/**
* Find module by its code with all data (types, selected type)
* #return mixed
*/
public function find($module_code)
{
$module = new ModuleService;
// Get module id in order to use build in relations in framework
$module = $this->module->find($module_code);
...
}
So, in other words, I have 2 classes and one interface. What I am trying to do is:
1) Create Class1.php / Class2.php / Class2Interface.php.
2) In Class1.php in the __construct I specify __construct(Class2Interface $class2).
3) Instantiate Class2.
What I am doing wrong? Examples found here.
In this line:
$module = new ModuleService;
You are directly invoking the Module class and not passing in an instance of SelectorInterface.
For the IoC to work you bind and make classes using it. Try that line again with :
$module = App::make('License\Services\ModuleSelector\SelectorInterface');
An alernative is to inject it directly into your repos constructor, as long as the repo is created by the IoC container, your concrete will be automatically injected.
Nowhere do you have a class marked to actually "implement SelectorInterface".

Zend Framework: Autoloading a Class Library

I've got a class library in defined here .../projectname/library/Me/Myclass.php defined as follows:
<?php
class Me_Myclass{
}
?>
I've got the following bootstrap:
<?php
/**
* Application bootstrap
*
* #uses Zend_Application_Bootstrap_Bootstrap
*/
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
/**
* Bootstrap autoloader for application resources
*
* #return Zend_Application_Module_Autoloader
*/
protected function _initAutoload()
{
$autoloader = new Zend_Application_Module_Autoloader(array(
'namespace' => 'Default',
'basePath' => dirname(__FILE__),
));
$autoloader->registerNamespace('Me_');
return $autoloader;
}
/**
* Bootstrap the view doctype
*
* #return void
*/
protected function _initDoctype()
{
$this->bootstrap('view');
$view = $this->getResource('view');
$view->doctype('XHTML1_STRICT');
}
/**
* Bootstrap registry and store configuration information
*
* #return void
*/
protected function _initRegistry()
{
$config = new Zend_Config_Ini(APPLICATION_PATH .
'/configs/application.ini', APPLICATION_ENV,
array('allowModifications'=>true));
Zend_Registry::set('configuration', $config);
}
}
In my controller I try to instantiate the class like this:
<?php
class SomeController extends Zend_Controller_Action
{
public function indexAction()
{
$classMaker=new Me_Myclass();
}
}
?>
When I navigate directly to http:/something.com/projectname/some?id=1 I get the following error:
Fatal error: Class 'Me_Myclass' not found in /home/myuser/work/projectname/application/controllers/SomeController.php on line x
Any ideas?
Potentially Pertinent Miscellany:
The autoloader seems to work when I'm extending models with classes I've defined in other folders under application/library.
Someone suggested changing the 'Default', which I attempted but it didn't appear to fix the problem and had the added negative impact of breaking function of models using this namespace.
You class needs to be name Me_Myclass:
class Me_Myclass
{
}
Move your library folder up a level so that you have the folder structure:
/
/application
/library
/public
And then in your Bootstrap add the following to the _initAutoload():
Zend_Loader_Autoloader::getInstance()->registerNamespace('Me_');
you can define the autoload dir in the config.ini file like this:
autoloaderNamespaces[] = "Me_"
;You could add as many as you want Classes dir:
autoloaderNamespaces[] = "Another_"
autoloaderNamespaces[] = "Third_"
works 100%
I think #smack0007 means replace the contents of your _initAutoload method with Zend_Loader_Autoloader::getInstance()->registerNamespace('Me_'); so it looks like this:
protected function _initAutoload()
{
Zend_Loader_Autoloader::getInstance()->registerNamespace('Me_');
}
Not sure if this is your problem, but I just spent the last day and half trying to figure out my own similar problem (first time loading it up on Linux from Windows). Turns out I was blind to my library's folder name case.
/library
/Tlib
is not the same as (on *nix)
/library
/tlib
Class name is typically this
class Tlib_FooMe {
...
}
Hope this helps someone who is similarly absentminded.

Categories