Yii is giving me 404 Error if I declare an action like this:
SiteController.php
public function actionRegisterUser()
This is how I call it in the main.php
['label' => 'Register User', 'url' => ['/site/RegisterUser']],
I tried several different combinations. The only combination that will work is this naming convention in both places:
public function actionRegisteruser
'url' => ['/site/registeruser']
I used to work on another Yii project (Yii 1.0) and I could name my actions in camel case and call them without any problem. Do I need to turn on some sort of setting to do this?
I also tried playing with the rules of the Controller but that didn't solve anything.
In some cases you need camelcase link. For example, for SEO purposes (keep inbound links). You could create rewrite rule on web server side or add inline rule to URL manager on app side. Example:
'urlManager' => [
'rules' => [
'<controller:RegisterUser>/<action:\w+>'=>'register-user/<action>',
],
],
Also it's possible to write custom URL rule. Example:
namespace app\components;
use yii\web\UrlRuleInterface;
use yii\base\Object;
class CarUrlRule extends Object implements UrlRuleInterface
{
public function createUrl($manager, $route, $params)
{
if ($route === 'car/index') {
if (isset($params['manufacturer'], $params['model'])) {
return $params['manufacturer'] . '/' . $params['model'];
} elseif (isset($params['manufacturer'])) {
return $params['manufacturer'];
}
}
return false; // this rule does not apply
}
public function parseRequest($manager, $request)
{
$pathInfo = $request->getPathInfo();
if (preg_match('%^(\w+)(/(\w+))?$%', $pathInfo, $matches)) {
// check $matches[1] and $matches[3] to see
// if they match a manufacturer and a model in the database
// If so, set $params['manufacturer'] and/or $params['model']
// and return ['car/index', $params]
}
return false; // this rule does not apply
}
}
And use the new rule class in the [[yii\web\UrlManager::rules]] configuration:
[
// ...other rules...
[
'class' => 'app\components\CarUrlRule',
// ...configure other properties...
],
]
You need to specify your action like this ['/site/register-user']. As documentation says about Inline Actions:
index becomes actionIndex, and hello-world becomes actionHelloWorld
Related
I am having a problem getting SilverStripe Fluent module to work with content/page controllers. Whenever a locale url segment is provided, the controller returns 404. For example, http://site.local/search works but http://site.local/en/search returns 404.
I tried using route config by pointing mi/search to the controller name. The template renders but the current locale is not correct.
To reproduce:
Set up a SilverStripe project using composer create-project silverstripe/installer test
Require the module composer require tractorcow/silverstripe-fluent
Setup 2 locale
English with url segment 'en'
Maori with url segment 'mi'
Create a simple controller called SearchController
Create a route.yml in the config folder
Create template file called Search.ss in the template folder
<?php
namespace App\Controllers;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\CMS\Controllers\ContentController;
class SearchController extends ContentController
{
private static $allowed_actions = [
'index',
];
public function index(HTTPRequest $request)
{
return $this->renderWith('Search');
}
}
---
Name: approutes
After: framework/_config/routes#coreroutes
---
SilverStripe\Control\Director:
rules:
'search//': 'App\Controllers\SearchController'
# 'mi/search//': 'App\Controllers\SearchController'
# 'en/search//': 'App\Controllers\SearchController'
<h1>Search</h1>
$CurrentLocale
Navigate to <baseurl>/mi/search, the template should render:
<h1>Search</h1>
mi_NZ
But error 404 is returned.
Concept
So Fluent supports localising via query string out of the box, but appending locales to the URL is reserved for pages driven by the CMS. With your example I'm able to see correct results via /search?l=en and /search?l=mi.
In order to allow locales in the URL for non-SiteTree routes, we can patch FluentDirectorExtension, which is the class responsible for injecting Fluent's routing rules, and add support for explicit configuration of routes that should also be localisable. This can be achieved by adding Director rules that essentially do the same thing as above, but masking the /search?l=en URL as /en/search in the background.
My example configuration is something like this:
TractorCow\Fluent\Extension\FluentDirectorExtension:
static_routes: # Routes that should also allow URL segment based localisation
- 'search//'
This should match the rule key in your Director.rules config.
We can then construct the new URL to allow support for, and tell Director to use the existing configured controller while also passing the l argument for the locale transparently. We need to do this for each locale, and the rules need to be inserted before Fluent's default rules are. An example of what you could do:
diff --git a/src/Extension/FluentDirectorExtension.php b/src/Extension/FluentDirectorExtension.php
index 6ebf1d6..0cdd80b 100644
--- a/src/Extension/FluentDirectorExtension.php
+++ b/src/Extension/FluentDirectorExtension.php
## -116,7 +116,10 ## class FluentDirectorExtension extends Extension
protected function getExplicitRoutes($originalRules)
{
$queryParam = static::config()->get('query_param');
+ $staticRoutes = static::config()->get('static_routes');
$rules = [];
+ $prependRules = []; // we push these into the $rules before default fluent rules
+
/** #var Locale $localeObj */
foreach (Locale::getCached() as $localeObj) {
$locale = $localeObj->getLocale();
## -138,8 +141,22 ## class FluentDirectorExtension extends Extension
'Controller' => $controller,
$queryParam => $locale,
];
+
+ // Include opt-in static routes
+ foreach ($staticRoutes as $staticRoute) {
+ // Check for a matching rule in the Director configuration
+ if (!isset($originalRules[$staticRoute])) {
+ continue;
+ }
+
+ $prependRules[$url . '/' . $staticRoute] = [
+ 'Controller' => $originalRules[$staticRoute],
+ $queryParam => $locale,
+ ];
+ }
}
- return $rules;
+
+ return array_merge($prependRules, $rules);
}
/**
If you debug $rules at the end of the updateRules() method, you'll see that Fluent has now injected a new rule for that route in each locale:
'en/search//' =>
array (size=2)
'Controller' => string 'App\Controllers\SearchController' (length=42)
'l' => string 'en_NZ' (length=5)
'mi/search//' =>
array (size=2)
'Controller' => string 'App\Controllers\SearchController' (length=42)
'l' => string 'mi_NZ' (length=5)
Implementation
I'm going to formulate a pull request to the module for this change once I can back it up with some unit tests, but in the meantime, you can implement this by using an Injector override in your project code, and extend the protected getExplicitRoutes method to implement the changes above:
SilverStripe\Core\Injector\Injector:
TractorCow\Fluent\Extension\FluentDirectorExtension:
class: MyFluentDirectorExtension
class MyFluentDirectorExtension extends FluentDirectorExtension
{
protected function getExplicitRoutes($originalRules)
{
$rules = parent::getExplicitRoutes($originalRules);
$staticRoutes = static::config()->get('static_routes');
$queryParam = static::config()->get('query_param');
$prependRules = [];
// Include opt-in static routes
foreach (Locale::getCached() as $localeObj) {
foreach ($staticRoutes as $staticRoute) {
$locale = $localeObj->getLocale();
$url = urlencode($localeObj->getURLSegment());
// Check for a matching rule in the Director configuration
if (!isset($originalRules[$staticRoute])) {
continue;
}
$prependRules[$url . '/' . $staticRoute] = [
'Controller' => $originalRules[$staticRoute],
$queryParam => $locale,
];
}
}
return array_merge($prependRules, $rules);
}
}
I'm developing Prestashop module, it will export customer data and orders, it will contain hooks for customer synchronization, cart and order events - generally module which will be an integration with CRM-like service.
My module contains it's own views, made in vue.js - single page, async. There are register, login, settings, etc. pages. Communication with backend is made by GET/POST requests on {baseUrl}/mymodule/actionname routes and simple json responses which vue views depend on. Simply I need to create REST endpoints for my module, something like examples below.
Wordpress custom RestApi:
class RestApi
{
public function __construct()
{
add_action('rest_api_init', array(get_class($this),
'register_endpoints'));
}
public static function register_endpoints()
{
register_rest_route('mymodule', '/login', array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array('RestApi', 'login' ),
));
}
}
SugarCRM custom RestApi:
class ModuleRestApi extends SugarApi
{
public function registerApiRest()
{
return [
'moduleLogin' => [
'reqType' => 'POST',
'noLoginRequired' => true,
'path' => [
'mymodule', 'login'
],
'method' => 'login'
],
];
}
}
I cannot find similar solution in PrestaShop, there is no word about custom endpoints in presta docs, I tried to use FrontModuleControllers with friendly url's but it doesn't seem to work for me, it throws a lot of stuff in response which is useless for me and when I try to override init() method it requires a lot of stuff too to actually initiate the controller. I need simple REST solution where I can put logic for recieving data from my views, pass it to my CRM service and return json responses to my views. I don't need any more templates or views rendering, just routing for cummunication.
PrestaShop doesn't support this out of the box. You can however do it with a module and front controllers.
This is a basic example of doing it.
1. Module to register friendly URLs
class RestApiModule extends Module
{
public function __construct()
{
$this->name = 'restapimodule';
$this->tab = 'front_office_features';
$this->version = '1.0';
parent::__construct();
}
public function install()
{
return parent::install() && $this->registerHook('moduleRoutes');
}
public function hookModuleRoutes()
{
return [
'module-restapimodule-login' => [
'rule' => 'restapimodule/login',
'keywords' => [],
'controller' => 'login',
'params' => [
'fc' => 'module',
'module' => 'restapimodule'
]
]
];
}
}
2. Create an abstract REST controller
Create an abstract controller so that actual endpoints can extend from it. Create it in your module controllers folder lets name it AbstractRestController.php
abstract class AbstractRestController extends ModuleFrontController
{
public function init()
{
parent::init();
switch ($_SERVER['REQUEST_METHOD']) {
case 'GET':
$this->processGetRequest();
break;
case 'POST':
$this->processPostRequest();
break;
case 'PATCH': // you can also separate these into their own methods
case 'PUT':
$this->processPutRequest();
break;
case 'DELETE':
$this->processDeleteRequest();
break;
default:
// throw some error or whatever
}
}
abstract protected function processGetRequest();
abstract protected function processPostRequest();
abstract protected function processPutRequest();
abstract protected function processDeleteRequest();
}
3. Create an actual front controller
Create the front controller in your module controllers/front folder and name it login.php.
require_once __DIR__ . '/../AbstractRestController.php';
class RestApiModuleLoginModuleFrontController extends AbstractRestController
{
protected function processGetRequest()
{
// do something then output the result
$this->ajaxDie(json_encode([
'success' => true,
'operation' => 'get'
]));
}
protected function processPostRequest()
{
// do something then output the result
$this->ajaxDie(json_encode([
'success' => true,
'operation' => 'post'
]));
}
protected function processPutRequest()
{
// do something then output the result
$this->ajaxDie(json_encode([
'success' => true,
'operation' => 'put'
]));
}
protected function processDeleteRequest()
{
// do something then output the result
$this->ajaxDie(json_encode([
'success' => true,
'operation' => 'delete'
]));
}
}
Install the module and now you can hit http://example.com/restapimodule/login and depending on the request type it's going to do whatever you want and you get back JSON response.
To add more endpoints add another module-restapimodule-endpointname entry into hookModuleRoutes array and a front controller that extends from AbstractRestController.
If you also want proper response codes etc. you're going to have to set headers with native php functions as PrestaShop afaik doesn't have any utilities to do it for you or use some kind of library.
Same goes for any other headers you might want to set such as content-type (by default it is text/html).
It is possible to use the Prestashop Webservice, that allows to add resources from modules. This solution could save some time in terms of standards and security.
The documentation regarding module resources in Prestashop Webservice is in this link:
https://webkul.com/blog/creating-prestashop-module-webservice-api/
I cant to make request to my controller in Yii2
I have controller /controllers/IndexController.php
class IndexController extends Controller
{
public function actionIndex()
{
return $this->render('index');
}
public function actionCreateAccount()
{
return Json::encode(array('status'=>'ok'));
}
}
In my config/web.php
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false
],
When I try to make request http://account.ll/Index/CreateAccount
I receive an error
Unable to resolve the request "Index/CreateAccount".
When I try to make request http://account.ll/Index I got the same error
Whats wrong?
It should be:
http://account.li/index/index or just http://account.li/index (because index is the default action). If the default controller is IndexController, you can access it like that - http://account.li/.
http://account.li/index/create-account
Controller and action names in actual url should be in lowercase. Action names containing more than one word are transformed with hyphens in between words.
Try to change
public function actionCreateAccount()
to
public function actionCreateaccount()
Just need to change in url from http://account.ll/Index/CreateAccount to http://account.ll/Index/create-account
My yii PHP project has UserController and it has an action called actionView. I can access user view page using following URL
mysite.com/user/view/id/1
I want to change that one to
mysite.com/username
How Can I do it.
I know that i can simply create rule to be more user friendly to get url such as
mysite.com/user/username
But url scheme with database resource name as direct param (mysite.com/username) is whole different story.
Url rule:
array(
'<username:\w+>'=>'user/view',
)
Note that in such scheme, you must also create rules for all your controllers and place above rule at the end, so better prefix it with user:
array(
'user/<username:\w+>'=>'user/view',
)
Resulting url will be example.com/user/username
In action:
public function actionView($username) ...
Update:
To make rule which reacts on any input variable create custom url rule class, here is some example, modify to your needs:
class PageUrlRule extends CBaseUrlRule
{
public function createUrl($manager, $route, $params, $ampersand)
{
// Ignore this rule for creating urls
return false;
}
public function parseUrl($manager, $request, $pathInfo, $rawPathInfo)
{
// Ignore / url or any controller name - which could conflict with username
if($pathInfo == '/')
{
return true;
}
// Search for username or any resource in db
// This is for mongo so it should be tuned to your db,
// just check if username exists
$criteria = new EMongoCriteria();
$criteria->url->$lang = $url;
$criteria->select(['_id']);
$criteria->limit(1);
$model = PageItem::model();
$cursor = $model->findAll($criteria);
// Create route, instead of $url id can be used
$route = sprintf('content/page/view/url/%s', urlencode($url));
// If found return route or false if not found
return $cursor->count() ? $route : false;
}
}
Then place this rule in beginning of urlmanager config
'rules' => [
[
'class' => 'application.modules.content.components.PageUrlRule'
],
// Other rules here
Important: If user has username same as your controller, it will match username and controller will be inaccessible. You must forbid registering users with same names as controllers.
I am currently trying to create a link on the index page that'll allow users to create an item. My routes.php looks like
Route::controller('items', 'ItemController');
and my ItemController looks like
class ItemController extends BaseController
{
// create variable
protected $item;
// create constructor
public function __construct(Item $item)
{
$this->item = $item;
}
public function getIndex()
{
// return all the items
$items = $this->item->all();
return View::make('items.index', compact('items'));
}
public function getCreate()
{
return View::make('items.create');
}
public function postStore()
{
$input = Input::all();
// checks the input with the validator rules from the Item model
$v = Validator::make($input, Item::$rules);
if ($v->passes())
{
$this->items->create($input);
return Redirect::route('items.index');
}
return Redirect::route('items.create');
}
}
I have tried changing the getIndex() to just index() but then I get a controller method not found. So, that is why I am using getIndex().
I think I have set up my create controllers correctly but when I go to the items/create url I get a
Unable to generate a URL for the named route "items.store" as such route does not exist.
error. I have tried using just store() and getStore() instead of postStore() but I keep getting the same error.
Anybody know what the problem might be? I don't understand why the URL isn't being generated.
You are using Route::controller() which does generate route names as far as I know.
i.e. you are referring to "items.store" - that is a route name.
You should either;
Define all routes specifically (probably best - see this blog here)
Use Route::resource('items', 'ItemController'); see docs here
If you use Route::resource - then you'll need to change your controller names
The error tells you, that the route name is not defined:
Unable to generate a URL for the named route "items.store" as such route does not exist.
Have a look in the Laravel 4 Docs in the Named Routes section. There are several examples that'll make you clear how to use these kind of routes.
Also have a look at the RESTful Controllers section.
Here's an example for your question:
Route::get('items', array(
'as' => 'items.store',
'uses' => 'ItemController#getIndex',
));
As The Shift Exchange said, Route::controller() doesn't generate names, but you can do it using a third parameter:
Route::controller( 'items',
'ItemController',
[
'getIndex' => 'items.index',
'getCreate' => 'items.create',
'postStore' => 'items.store',
...
]
);