Yii2 use runAction() within beforeAction method - yii2

I have a MainController which extends Controller. All my app's controllers extend from MainController which includes various methods and properties which need to be accessible from any Controller.
Within my MainController is beforeAction, which does several things:
Checks for redirects held in the database and performs them if the URL matches one in the DB.
Generates <head> data for each controller
Gets the language and country the user is looking at based on cookie and slug of the URL. (i.e. http://example.com/netherlands).
Will render a generic page from a template if URL matches one from the database's pages table.
It's the last that I am struggling with. In my MainController I have this:
/**
* Before action, check all $this->before_actions satisfy. If no head_data provided, try and fill in some basics
*/
public function beforeAction( $action )
{
// Run parent method first
if (!parent::beforeAction($action))
return false;
// Check redirects
$this->checkRedirects();
if( $this->checkPages() )
{
// If not error page, loop through before methods
if( $action->id !== 'error' )
{
// Loop through actions to peform and do them
foreach ( $this->before_actions as $before_method )
$this->$before_method();
}
return true;
}
}
Where $this->checkPages() contains the following:
/**
* Check for pages
*/
public function checkPages()
{
// Attempt to find page for this request
$page = Page::find()->where( [ 'permalink' => trim( str_replace( getBaseUrl() , "", getCurrentUrl() ), "/" ) ] )->one();
// If found, load it instead
if( !empty( $page ) )
return Yii::$app->runAction( "pages/show", [ 'id' => $page->id ] );
// Else, return
return true;
}
The issue I am having is that if I go to http://example.com/story, because there is no StoryController, the returns a 404 error although the action does run and the view "views/story/show" is output.
How can I prevent this?
EDIT:
To add, the log shows that it first says:
"Unable to resolve the request 'story/index'".
But then additional logs show:
"Route to run: pages/show" ... "Running action: app\controllers\PagesController::actionShow()"
..
Rendering view file: /Users/stefandunn/Documents/Local Machine Development/views/pages/show.php
So I am guessing it's the first log result causing the 404 status

Add one last route that can catch any pattern and redirect to custom action.
'urlManager' => [
'class' => 'yii\web\UrlManager',
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
//...
'<any:.*>' => 'site/index'
],
],

Related

Yii2: Replace querystring parameter with value using urlManager

I have a rule in urlManager
'rules' => [
'products/<whatever:[-_0-9a-zA-Z]+>' => 'products/show',
],
and I have controller Products with actionShow()
public function actionShow($name)
{
var_dump($name);
echo 'actionShow is work';
}
But a have an Error: Bad Request (#400) Missing required parameters: name
How to transfer a product's $name to the controller, or how it can be obtained from the URL in the controller?
The CORRECT and COMPLETE answer is that you have to use the rule like below.
'rules' => [
'products/<name:[\-\w]+>' => 'products/show',
],
then in your products controller you don't need to pass $name in the parameter anymore that is why we defined in the rule and you have to use the
Yii::$app->request->queryParams['name']; or Yii::$app->request->get('name') to get the name of the product, your action will look like below
public function actionShow()
{
echo Yii::$app->request->get('name');
}
after doing all the settings above open brower and type
http://yourdomain.com/products/some-product
and it will show you
some-product
When you have to create a link to that page lets say in menu you have under products all products listed and you have to create a link to detail page for all of them i.e actionShow you would do it like below.
<?=Html::a($model->name,
Yii::$app->urlManager->createUrl([
'products/show',
'name'=>$model->slug
])
);
?>
I guess you should specify the name of your variable in the rule. You are calling it whatever, change it to name:
'rules' => [
'products/<name:[-_0-9a-zA-Z]+>' => 'products/show',
],

CakePHP 3 - Can't return proper json when debug mode = true

I'm new to stackoverflow, and I've just started to play around with CakePHP 3.
I've run into a weird problem:
I'm sending an ajax-request (form submit) to the controller, and I expect to get a proper json-response back. It works fine when I set debug mode to false in config/app.php, but when it's set to true, I get an error-message in the browsers console, and the responsetext seem to be html. I'm calling the action with the .json extension in the url.
I've linked screenshot of the console where the first response is with debug mode set to false, and the second set to true:
I have enabled the extensions in config/routes.php:
Router::scope('/', function (RouteBuilder $routes) {
$routes->extensions(['json', 'xml']);
(...)
Here's the controller-code:
public function getUserStats() {
$this->log($this->request->data, 'debug');
if (($this->request->is('post'))) {
$this->log('getCategories(): Post-request is received.', 'info');
$usersTable = TableRegistry::get('Users');
$q = $usersTable->find('statsByUsers', $this->request->data);
$users = $q->all();
// Calculating total amount per user.
foreach ($users as $u) {
foreach ($u->purchases as $p) {
$u->total += $p->total;
}
}
$this->log($users, 'debug');
$this->set('users', $users);
$this->set('_serialize', ['users']);
}
}
Here's the model code:
public function findStatsByUsers(Query $query, array $options) {
debug($options);
$options['dates'] = $this->getConvertedDates($options);
$query
->contain([
'Purchases' => function($q) use($options) {
return $q
->select(['id', 'total' => 'amount * count', 'purchase_date', 'user_id'])
->where(['purchase_date BETWEEN :fromDate AND :toDate',])
->bind(':fromDate', $options['dates']['fromDate'], 'datetime') // Binds the dates to the variables in where-conditions
->bind(':toDate', $options['dates']['toDate'], 'datetime');
}
])
->where([
'Users.id IN ' => $options['users'],
'Users.active' => true
]);
return $query;
}
I hope I've given you enough information so that you can help me solve this.
CakePHP version: 3.3.2
Looking at the bit of output that is visible in the screenshot
<div class="cake-debug-output"> ...
that HTML is output generated by the debug() function.
Look closely at your model code, and you should spot the call to the function. Remove it, and you should be good.
btw, the source of the call can be found in the first <span> element in the <div>, so if you experience similar problems in the future make sure to check that.
<?php
use Cake\Core\Configure;
// your class ,...
public function getUserStats() {
$this->log($users, 'debug');
Configure::write('debug',false); // DISABLE
$this->set('users', $users);
$this->set('_serialize', ['users']);
}

All Yii2 controller not allow action without login or guest must need login

I'm using Yii2 Advance application and i am new in yii2 so how make
all yii2 controller not allow action without login or guest must me login
i mean controllers can not open without login if user not login so redirect in login page this not for one controller i need many controller
You need to add below code in common/main.php after components part.
'as beforeRequest' => [ //if guest user access site so, redirect to login page.
'class' => 'yii\filters\AccessControl',
'rules' => [
[
'actions' => ['login', 'error'],
'allow' => true,
],
[
'allow' => true,
'roles' => ['#'],
],
],
],
You could simply create a super controller for this :
class Controller extends \yii\web\Controller
{
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'allow' => false,
'roles' => ['?'],
],
],
],
];
}
}
And of course all your controllers should extend this one.
Read more about Access Control Filter.
You can try write in index.php in your backed public directory
No need to repeat in controllers
<?php
// comment out the following two lines when deployed to production
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
require __DIR__ . '/../../vendor/autoload.php';
require __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';
$config = require __DIR__ . '/../../config/web.php';
$config["controllerNamespace"]='app\controllers\backend';
(new yii\web\Application($config))->run();
if(Yii::$app->user->isGuest){
$request_headers = apache_request_headers();
$srv=$request_headers['Host'];
header("Location: https://".$srv);
die();
}
use RBAC Manager for Yii 2 you can Easy to manage authorization of user.
https://github.com/mdmsoft/yii2-admin.
You could inherit from the yii Controller class where you can override the beforeAction method and in that function you can redirect the user if there is no active logged in session.
Make sure that all of the other controllers what you are using inherited from your new controller class.
EDIT:
In the SuperController beforeAction you can check if the current call is your site/index, if not then redirect to there like:
if($action->controller != "site" && $action->id != "index" ) {
$this->goHome();
}
Make sure that the goHome() take you to your site/index.
And you can split your Site actionIndex() method to an authenticated or not part like:
public function actionIndex() {
if(Yii::$app->user->isGuest) {
return $this->redirect(Url::toRoute('site/login'));
}
else {
...
}
}
Hope this helps.

how to pass alphanumeric parameter in yii2 controller action

My code is
class WorkshopsController extends Controller
{
public $layout = false;
public function actionIndex($address)
{
echo $address;
return $this->render('workshops.twig');
}
}
I want to pass any place address to above given action. how can i do that in Yii2. Address can be like "Mazyad Road, Mazyad - Al Ain - أبو ظبي - United Arab Emirates"
You need to request you workshops/index page with address query parameter example.com/workshops/index?address=some address. Yii will automaticaly call your action and pass query params into it.
EDIT
If you wish to use pretty url, you must specify urlManager rules for it. E.g.
rules => [
'workshops/<address>' => 'workshops/index',
],
Also if you wish your page open when addess parameter not specified you can define rules like this:
[
'pattern' => 'workshops/<address>',
'route' => 'workshops/index',
'defaults' => ['address' => ''],
]

zend framework 2, return the rendered contents of a view inside a JSON Model

I am trying to create a JsonModel with an item in the variables 'html' containing the current rendered view. I would like to add this code to an event:
rather than this method: How to render ZF2 view within JSON response? which is in the controller, I would like to automate the process by moving it to an Event
I have the strategy in my module.config.php:
'strategies' => array(
'ViewJsonStrategy',
)
I have set up a setEventManager in the controller:
$events->attach(MvcEvent::EVENT_RENDER, function ($e) use ($controller) {
$controller->setRenderFormat($e);
}, -20);
Is this the best event to attach it to? would the RENDER_EVENT be better?
Now I would like to change the render of the page based on !$this->getRequest()->isXmlHttpRequest(), (commented out for debug)
public function setRenderFormat($e)
{
//if(!$this->getRequest()->isXmlHttpRequest())
//{
$controller = $e->getTarget();
$controllerClass = get_class($controller);
//Get routing info
$controllerArr = explode('\\', $controllerClass);
$currentRoute = array(
'module' => strtolower($controllerArr[0]),
'controller' => strtolower(str_replace("Controller", "", $controllerArr[2])),
'action' => strtolower($controller->getEvent()->getRouteMatch()->getParam('action'))
);
$view_template = implode('/',$currentRoute);
$viewmodel = new \Zend\View\Model\ViewModel();
$viewmodel->setTemplate($view_template);
$htmlOutput = $this->getServiceLocator()->get('viewrenderer')->render($viewmodel, $viewmodel);
$jsonModel = new JsonModel();
$jsonModel->setVariables(array(
'html' => $htmlOutput,
'jsonVar1' => 'jsonVal2',
'jsonArray' => array(1,2,3,4,5,6)
));
return $jsonModel;
//}
}
Strangely, (or not) this code works and produces the $jsonModel, however is doesn't overwite the normal HTML view with the json, but the same code (without the event) in a controller method, overwrites perfectly.
p.s Is there a better method to do the whole concept?
p.p.s how can I obtain the current View Template from within the controller, without resorting to 8 lines of code?
Thanks in advance!
Aborgrove
you are returning the view model from an event I thinks this doesn't have any effect in current viewmanager view model, fetch the current viewmodel from viewmanager and call setTerminal(true). or replace the created jsonmodel using the viewmanager