rewriting url with updating url in yii2 - php

how change this:
site/faq
to this:
site/faq?festival=nouroz98&id=100000&data=information
this is the FestivalRule class
class FestivalRule implements UrlRuleInterface
{
public $pattern;
public $route;
public function createUrl($manager, $route, $params)
{
return [$route, $params];
}
public function parseRequest($manager, $request)
{
if(!empty($request->getQueryParams())){
if($request->getPathInfo() == 'site/faq'){
return [
'site/faq',
$request->getQueryParams()
];
}
}else{
return [
'site/faq',
[
'festival' => 'nouroze99',
'id' => '10000',
'data' => 'from db',
]
];
}
return false;
}
}
this is the urlmanager config:
'rules' => [
[
'class' => 'app\components\FestivalRule',
'pattern' => 'site/faq/<festival:\w+>/<id:\d+>/<data:\w+>',
'route' => 'site/faq',
],
I want if the URL requested without any parameters i changing that with parameters but i can't place the parameters in URL.
all parameters sent but i wanted url changes too (parameters shows in URL)!

Related

Remove empty array in Resource::Collection not working Laravel

I want to remove empty array when its return. I have been trying in many different ways, help plz
My controller looks :
public function index()
{
return JobsResource::collection(Jobs::all())->filter();
}
my resource file look:
class JobsCollection extends Resource
{
public function toArray($request)
{
$applicants_count =Job_applicants::where('job_id',$this->id)->get()->count();
if ($applicants_count>0) {
return [
'id' => $this->id,
'title' => $this->title,
'deadline' => $this->deadline,
'applicants_count' => $applicants_count,
'applicants' => new EmployeesResource($this->Employeess->take(2))
];
}
}
}
it always return an empty array
output :
[
[],
{
"id":99,
"title":"Construction Administrator - The Woodlands",
"deadline":"2018-06-30",
"applicants_count":10,
"applicants":[
{
"name":"Mr. Job Seeker",
"pivot":{
"job_id":99,
"employee_id":1
}
},
{
"name":"Michale Feil",
"pivot":{
"job_id":99,
"employee_id":2
}
}
]
}
Controller:
public function index() {
$jobs = Jobs::has('Employeess')->with('Employeess')->withCount('Employeess')->get();
return JobsResource::collection($jobs);
}
Resource file:
class JobsCollection extends Resource
{
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'deadline' => $this->deadline,
'applicants_count' => $this->Employeess_count,
'applicants' => new EmployeesResource($this->Employeess->take(2))
];
}
}

"NetworkError: 405 Method Not Allowed" in YII2 rest API

I am working on Yii2 rest API, When I call create action of enquiryontroller then I am getting this error : "NetworkError: 405 Method Not Allowed".
And also I go through YII2 documentation but not able to trace my issue.
Please check and revert, it will be a great help.
Here is controller code that is EnquiryController.php :
<?php
namespace frontend\controllers;
use Yii;
use common\models\Enquiry;
use yii\filters\ContentNegotiator;
use yii\web\Response;
use yii\filters\AccessControl;
use yii\rest\ActiveController;
use yii\filters\auth\HttpBearerAuth;
use yii\filters\VerbFilter;
use yii\data\ActiveDataProvider;
class EnquiryController extends ActiveController
{
/**
* #inheritdoc
*/
public $modelClass = 'common\models\Enquiry';
public $serializer = [
'class' => 'yii\rest\Serializer',
'collectionEnvelope' => 'items',
];
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => HttpBearerAuth::className(),
];
$behaviors['contentNegotiator'] = [
'class' => ContentNegotiator::className(),
'formats' => [
'application/json' => Response::FORMAT_JSON,
],
];
return $behaviors;
}
public function actions()
{
$actions = parent::actions();
// disable the "delete" and "create" actions
unset($actions['create']);
unset($actions['delete'], $actions['view']);
unset($actions['index']);
// customize the data provider preparation with the "prepareDataProvider()" method
return $actions;
}
public function actionCreate()
{
$model = new Enquiry();
return Yii::$app->getRequest()->getBodyParams();
if ($model->load(Yii::$app->getRequest()->getBodyParams(), '') && $model->validate()) {
$model->slug = \common\components\Helper::slugify($model->title);
$model->user_id = Yii::$app->user->id;
$model->save();
//mail functionality
return true;
}
return $model;
}
}
and code in config/main-local.php :
'urlManager' => [
'class' => 'yii\web\UrlManager',
'baseUrl' => $baseUrl,
'enablePrettyUrl' => true,
'showScriptName' => false,
//'enableStrictParsing' => true,
'rules' => [
['class' => 'yii\rest\UrlRule', 'controller' =>['api'], 'pluralize'=>true],
],
],
],
'as access' => [
'class' => 'mdm\admin\components\AccessControl',
'allowActions' => [
'site/*',
'api/login',
'profile/*',
'api/activate-user',
'api/contact',
'home/*',
'post/*',
'pages/*',
'categories/*',
'guestbook/*',
'faq/*',
'news/*',
'events/*',
'enquiry/*',
'partners/*',
'api/signup'// add or remove allowed actions to this list
]
],
Have a look to this guide
// disable the "delete" and "create" actions
unset($actions['delete'], $actions['create']);
because in your code you disable the create, delete, view and index action
public function actions()
{
$actions = parent::actions();
// disable the "delete" and "create" actions ?????
unset($actions['create']); ////????
unset($actions['delete'], $actions['view']); /// ????
unset($actions['index']); ////????
// customize the data provider preparation with the "prepareDataProvider()" method
return $actions;
}

Passing values to multiple views

I've this code for the controller (I'm using the basic template for testing purposes:
use Yii;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\filters\VerbFilter;
use app\models\LoginForm;
use app\models\ContactForm;
class SiteController extends Controller
{
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'only' => ['logout'],
'rules' => [
[
'actions' => ['logout'],
'allow' => true,
'roles' => ['#'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'logout' => ['post'],
],
],
];
}
public function actions()
{
return [
'error' => [
'class' => 'yii\web\ErrorAction',
],
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
],
];
}
public function actionIndex()
{
$param = $this->somefunction();
return $this->render('index', [
"param" => $param
]);
}
public function actionLogin()
{
if (!\Yii::$app->user->isGuest) {
return $this->goHome();
}
$model = new LoginForm();
if ($model->load(Yii::$app->request->post()) && $model->login()) {
return $this->goBack();
} else {
return $this->render('login', [
'model' => $model,
]);
}
}
public function actionLogout()
{
Yii::$app->user->logout();
return $this->goHome();
}
public function actionContact()
{
$param = $this->somefunction();
$model = new ContactForm();
if ($model->load(Yii::$app->request->post()) && $model->contact(Yii::$app->params['adminEmail'])) {
Yii::$app->session->setFlash('contactFormSubmitted');
return $this->refresh();
} else {
return $this->render('contact', [
'model' => $model,
"param" => $param
]);
}
}
public function actionAbout()
{
$param = $this->somefunction();
return $this->render('about', [
"param" => $param
]);
}
}
As you can notice, I'm passing param to multiple views with the same content so I want some time-saving way to send it only one time to all views.
Is that possible?
Currently, I'm using the session to store values and call the session in required views. I want something more convenience
You can use Controller and View events to achieve that.
Add this to your controller:
use yii\web\View;
...
public function beforeAction($action)
{
if (!parent::beforeAction($action)) {
return false;
}
$this->view->on(View::EVENT_BEFORE_RENDER, function() {
$this->view->params['param'] = ...;
});
return true;
}
If you want it in multiple controllers, either create another controller (extending from yii\web\Controller) for that and override beforeAction and then extend your controllers from custom one, or set it during application boostrap using BoostrapInterface.
Additionally you can add some condition:
if (in_array($action, ['create', 'update']) {
...
}
and custom param will be only passed in these actions.
in your controller you can redefine the init () function thanks to which you can assign to a variable of the controller the value you're interested

Yii2 Restfull APi RBAC Guest login

I have a rest full API in Yii2. I have the user model, controller and created a new rest action called login.
How can I set the action login to be executed by the guest users?
class UserController extends \yii\rest\ActiveController{
public function actions() {
$actions = parent::actions();
$actions['login'] = [
'class' => 'app\modules\user\actions\user\LoginUserAction',
'modelClass' => $this->modelClass,
'checkAccess' => [$this, 'checkAccess'],
];
return $actions;
}
public function checkAccess($action, $model = null, $params = array()) {
return true;
}
}
You can set the access control rules on the behaviors function of your controller, instead of on the checkAccess property on the action:
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['access'] = [
'class' => AccessControl::className(),
'except' => ['login'],
'rules' => [
[
'allow' => true,
'actions' => ['foo', 'foo2'],
'roles' => ['admin'],
]
],
];
return $behaviors;
}
In this example, the access control is applied to all actions except 'login'. I left the rules part so you can have an example of how you make customized rules.

Allow only author to edit his post in yii2 using ACF

I am using Access Control Filter for access managing, but can't get one thing done - for example, how can I allow just project manager to update project and forbid it to others? I tried it via matchCallback, but in this case all project managers can update any project because TRUE is returned.
Similar more often required rules - how to allow user to update/delete posts where he is author using ACF?
'access' => [
'class' => AccessControl::className(),
'only' => ['index', 'view', 'create', 'update', 'delete'],
'rules' => [
[
'actions' => ['update'],
'allow' => true,
'roles' => ['#'],
'matchCallback' => function ($rule, $action) {
return Yii::$app->user->identity->getProjectParticipants()
->one()->isManager(Yii::$app->user->identity->id);
}
],
],
],
It could be implemented something like this:
use Yii;
use yii\web\Controller;
use yii\filters\AccessControl;
class MyController extends Controller
{
...
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'only' => ['update', 'delete'],
'rules' => [
[
'actions' => ['update', 'delete'],
'allow' => true,
'roles' => ['#'],
'matchCallback' => function ($rule, $action) {
if (Yii::$app->user->can('admin') || $this->isUserAuthor()) {
return true;
}
return false;
}
],
],
],
];
}
protected function findModel($id)
{
if (($model = MyModel::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
protected function isUserAuthor()
{
return $this->findModel(Yii::$app->request->get('id'))->author->id == Yii::$app->user->id;
}
...
}
This is bested solved with a custom AccessRule. One would have to fill in the code to check if a user is the author of a project.
namespace app\filters;
class AuthorAccessRule extends \yii\filters\AccessRule
{
public $allow = true; // Allow access if this rule matches
public $roles = ['#']; // Ensure user is logged in.
public function allows($action, $user, $request)
{
$parentRes = parent::allows($action, $user, $request);
// $parentRes can be `null`, `false` or `true`.
// True means the parent rule matched and allows access.
if ($parentRes !== true) {
return $parentRes;
}
return ($this->getProjectAuthorId($request) == $user->id);
}
private function getProjectAuthorId($request)
{
// Fill in code to receive the right project.
// assuming the project id is given à la `project/update?id=1`
$projectId = $request->get('id');
$project = \app\models\Project::findOne($projectId);
return isset($project) ? $project->author_id : null;
}
}
The rule can be used by including this in the behaviors:
'authorAccess' => [
'class' => AccessControl::className(),
'only' => ['update'],
'rules' => ['actions' => ['update']],
'ruleConfig' => ['class' => '\app\filters\AuthorAccessRule'],
],
Following is how I do it with combination of ACF and RBAC. Please correct me if I am wrong or there is better way of doing it. It's based on Basic template.
User's role is stored in the "role" column of the "user" table. Another table "country" is used in this example. Assume you have generated models and controllers using Gii.
Customise PhpManager to use role from database table "user".
class PhpManager extends \yii\rbac\PhpManager
{
public function init()
{
parent::init();
}
public function getAssignments($userId)
{
if (!Yii::$app->user->isGuest) {
$assignment = new Assignment();
$assignment->userId = $userId;
# Assume the role is stored in User table "role" column
$assignment->roleName = Yii::$app->user->identity->role;
return [$assignment->roleName => $assignment];
}
}
}
3. Add authManager to web.app and console.app console file.
'authManager' => [
'class' => 'app\components\PhpManager',
'defaultRoles' => ['user', 'manager', 'admin', 'master'],
],
Create a customized AccessRule. Reference from speixoto's blog.
# Reference
# http://programming.peixoto.cf/2015/01/14/yii2-role-based-access-control-and-context-access-rule/#$$nmvkr0&&0SUmhOPVEeSW9grIhAgzZg$$
class ContextAccessRule extends AccessRule
{
public $modelClass;
public $primaryKey;
protected function matchRole($user)
{
if (parent::matchRole($user))
return true;
$model = $this->findModel();
foreach ($this->roles as $role) {
# Call the CheckAccess() function which process rules
if ($user->can($role, ['model' => $model])) {
return true;
}
}
return false;
}
protected function findModel()
{
if (!isset($this->modelClass))
throw new InvalidConfigException(Yii::t('app', 'the "modelClass" must be set for "{class}".', ['class' => __CLASS__]));
$primaryKey = $this->getPrimaryKey();
# Get the request params
$queryParams = \Yii::$app->getRequest()->getQueryParams();
# Load the model
$model = call_user_func([$this->modelClass, 'findOne'], $queryParams[join(',', $primaryKey)]);
if ($model !== null) {
return $model;
} else {
throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exists.'));
}
}
# Get the primary key of the model
protected function getPrimaryKey()
{
if (!isset($this->primaryKey)) {
return call_user_func([$this->modelClass, 'primaryKey']);
} else {
return $this->primaryKey;
}
}
Create a RbacController.php to generate RBAC files (assignments.php, items.php, rules.php) into rbac folder.
class RbacController extends Controller
{
public function actionInit()
{
$auth = Yii::$app->authManager;
$auth->removeAll();
### CREATE & ADD ROLES
$user = $auth->createRole('user');
$node = $auth->createRole('node');
$manager = $auth->createRole('manager');
$admin = $auth->createRole('admin');
$master = $auth->createRole('master');
$auth->add($user);
$auth->add($node);
$auth->add($manager);
$auth->add($admin);
$auth->add($master);
$auth->addChild($manager, $user);
$auth->addChild($manager, $node);
$auth->addChild($admin, $manager);
$auth->addChild($master, $admin);
### ADD RULES
$ownerRule = new \app\components\OwnerRule();
$auth->add($ownerRule);
### CREATE PERMISSIONS ###
$pUpdateOwn = $auth->createPermission('updateOwn');
$pUpdateOwn->description = 'update own';
$pUpdateOwn->ruleName = $ownerRule->name;
$auth->add($pUpdateOwn);
$auth->addChild($pUpdateOwn, $pUpdate);
$pDeleteOwn = $auth->createPermission('deleteOwn');
$pDeleteOwn->description = 'delete own';
$pDeleteOwn->ruleName = $ownerRule->name;
$auth->add($pDeleteOwn);
$auth->addChild($pDeleteOwn, $pDelete);
### ASSIGN PERMISSION TO ROLES
$auth->addChild($user, $pUpdateOwn);
$auth->addChild($user, $pDeleteOwn);
$auth->addChild($manager, $pUpdateOwn);
$auth->addChild($manager, $pDeleteOwn);
}
}
From console, navigate to your project root. Run ./yii rbac/init (for mac) to generate the 3 files into rbac folder.
In CountryController.php, override following function to add "access" behaviors.
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['verbs'] = [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['post'],
],
];
['access'] = [
'class' => AccessControl::className(),
// 'only' => ['view', 'index', 'create', 'update', 'delete'],
'rules' => [
[
'actions' => ['view', 'index'],
'allow' => true,
'roles' => ['?', '#'],
],
[
'actions' => ['create'],
'allow' => true,
// Allow users, manager and admins to create
'roles' => ['user'],
],
[
'class' => 'app\components\ContextAccessRule',
'modelClass' => 'app\models\Country',
'actions' => ['update'],
'allow' => true,
# allow owner and manager to udpate
'roles' => ['updateOwn', 'manager']
],
[
'class' => 'app\components\ContextAccessRule',
'modelClass' => 'app\models\Country',
'actions' => ['delete'],
'allow' => true,
# allow owner and manager to delete
'roles' => ['deleteOwn', 'manager'],
],
],
# if user not login, and not allowed for current action, return following exception
'denyCallback' => function ($rule, $action) {
throw new UnauthorizedHttpException('You are not authorized.');
},
];
return $behaviors;
}
8. Test it out.

Categories