Yii2 backend url manager rules for module - yii2-advanced-app

I have created a module in Yii2 using Gii code generator. My new generated module location is
backend/modules/cms
cms is new generated module name.
backend/config/main.php after setting module configuration looks as following
return [
'id' => 'app-backend',
'basePath' => dirname(__DIR__),
'controllerNamespace' => 'backend\controllers',
'bootstrap' => ['log'],
'modules' => [
'gii' => [
'class' => 'yii\gii\Module', //adding gii module
'allowedIPs' => ['127.0.0.1', '::1'] //allowing ip's
],
'cms' => [
'class' => 'backend\modules\cms\Cms',
],
],
'components' => [
'user' => [
'identityClass' => 'common\models\User',
'enableAutoLogin' => true,
],
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [
[
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning'],
],
],
],
'errorHandler' => [
'errorAction' => 'site/error',
],
'urlManager' => [
'class' => 'yii\web\UrlManager',
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
'<controller:\w+>/<action:\w+>/<id:\w+>' => '<controller>/<action>',
'<controller:\w+>/<action:\w+>' => '<controller>/<action>',
'<module:\w+>/<controller:\w+>/<action:\w+>' => '<module>/<controller>/<action>',
'<module:\w+><controller:\w+>/<action:update|delete>/<id:\d+>' => '<module>/<controller>/<action>',
]
],
'assetManager' => [
'bundles' => [
'yii\web\JqueryAsset' => [
'js' => []
],
],
],
],
'params' => $params,
];
behaviors function in controller looks as after setting access rules
public function behaviors() {
return [
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'actions' => ['login', 'error'],
'allow' => true,
],
[
'actions' => ['logout', 'index', 'create', 'update', 'delete'],
'allow' => true,
'roles' => ['#'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['post'],
],
],
];
}
Only index action of my controller is accessible. when i access any other action in controller url changes but shows index action contents.
How can i access all actions in controller?
It will be appreciable if someone help me resolving this issue.
my controller
namespace backend\modules\cms\controllers;
use Yii;
use yii\filters\AccessControl;
use backend\modules\cms\models\Pages;
use backend\modules\cms\models\PagesSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
/**
* PagesController implements the CRUD actions for Pages model.
*/
class PagesController extends Controller
{
public function behaviors() {
return [
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'actions' => ['login', 'error'],
'allow' => true,
],
[
'actions' => ['logout', 'index', 'create', 'update', 'delete'],
'allow' => true,
'roles' => ['#'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
// 'delete' => ['post'],
],
],
];
}
/**
* Lists all Pages models.
* #return mixed
*/
public function actionIndex()
{
$searchModel = new PagesSearch();
$dataProvider = $searchModel->search(Yii::$app->request->post());
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single Pages model.
* #param integer $id
* #return mixed
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new Pages model.
* If creation is successful, the browser will be redirected to the 'view' page.
* #return mixed
*/
public function actionCreate()
{
echo "here";
exit;
$model = new Pages();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['index']);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
/**
* Updates an existing Pages model.
* If update is successful, the browser will be redirected to the 'view' page.
* #param integer $id
* #return mixed
*/
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('update', [
'model' => $model,
]);
}
}
/**
* Deletes an existing Pages model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* #param integer $id
* #return mixed
*/
public function actionDelete($id)
{
$this->findModel($id)->delete();
return $this->redirect(['index']);
}
/**
* Finds the Pages model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* #param integer $id
* #return Pages the loaded model
* #throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = Pages::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
}
I am using Url as follows:
http://localhost/yii2-demo/backend/cms/pages/index
http://localhost/yii2-demo/backend/cms/pages/create

What Url(s) are you using?
For a module it should be:
?r=cms/pages/index,
?r=cms/pages/create
etc...

The only problem I can spot is on your $matchCallback attribute on the second AccessControl rule. It should use this signature
function ($rule, $action)
And it should return a boolean that indicates wheter this rule should be applied or not. In your case, you're saying that it should be applied to users that are not guests and that have the role '0'. You're also calling a redirect inside the function, and it doesn't return a boolean. See more info here http://www.yiiframework.com/doc-2.0/yii-filters-accessrule.html#$matchCallback-detail
Try to remove this attribute completely and see if you get a different result.

Related

Laravel 8 Multi user Authentication JWT

I have to models 'User' and 'Admin'. 'User' is the default model in Laravel. I want to authenticate these models separately.
class Admin extends Authenticatable implements JWTSubject
{
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'email',
'name',
'contact',
'password'
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
public function getJWTIdentifier()
{
return $this->getKey();
}
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* #return array
*/
public function getJWTCustomClaims()
{
return [];
}
}
In auth.php,
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
'hash' => false,
],
'admins' => [
'driver' => 'jwt',
'provider' => 'admins',
],
'admin-api' => [
'driver' => 'jwt',
'provider' => 'admins',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
'admins' => [
'driver' => 'eloquent',
'model' => App\Models\Admin::class,
],
],
In AdminController.php,
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth:admins');
}
public function register(Request $request){
$creds = [
'email' => $request->input('email'),
'name' => $request->input('name'),
'password' => bcrypt($request->input('password')),
'contact' => $request->input('contact'),
];
try {
$admin = Admin::create($creds);
} catch (\Throwable $th) {
return response(
[
'message' => $th,
'success' => false
],
);
}
event(new Registered($admin));
$token = JWTAuth::fromUser($admin);
return response(
[
'user' => $admin,
'message' => 'register success',
'token' => $token,
'success' => true
],
201
);
}
public function login(Request $request){
$creds = [
'email' => $request->input('email'),
'password' => $request->input('password')
];
if(!Auth()->attempt($creds)){
return response([
'message' => 'login faild!',
'success' => false
]);
}
$admin = Auth()->user();
$token = JWTAuth::fromUser($admin);
return response(
[
'user' => $admin,
'token' => $token,
'success' => true
],
201
);
}
can you help me?
I do not know how to use register and login function correctly. Can you explain what is happening in those functions. How to use a middleware correctly?

zf3 __construct() not working

I've created a module with name Commerce in zend 3 which is working fine. Now when I inject a dependency through __construct() it throws error
Too few arguments to function
Commerce\Controller\IndexController::__construct(), 0 passed in
/var/www/html/zf3/vendor/zendframework/zend-servicemanager/src/Factory/InvokableFactory.php
on line 30 and exactly 1 expected
Here is the controller code.
<?php
namespace Commerce\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Commerce\Model\Commerce;
class IndexController extends AbstractActionController
{
private $commerce;
/**
* IndexController constructor.
* #param Commerce $commerce
*/
public function __construct(Commerce $commerce)
{
$this->commerceModel = $commerce;
}
public function indexAction()
{
return new ViewModel();
}
}
module.config.php code
<?php
namespace Commerce;
use Zend\Router\Http\Literal;
use Zend\Router\Http\Segment;
use Zend\ServiceManager\Factory\InvokableFactory;
return [
'router' => [
'routes' => [
'home' => [
'type' => Literal::class,
'options' => [
'route' => '/',
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'index',
],
],
],
'commerce' => [
'type' => Segment::class,
'options' => [
'route' => '/commerce[/:action][/:id]',
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'index',
],
],
],
],
],
'controllers' => [
'factories' => [
Controller\IndexController::class => InvokableFactory::class
],
],
'view_manager' => [
'display_not_found_reason' => true,
'display_exceptions' => true,
'doctype' => 'HTML5',
'not_found_template' => 'error/404',
'exception_template' => 'error/index',
'template_map' => [
'layout/layout' => __DIR__ . '/../view/layout/layout.phtml',
'commerce/index/index' => __DIR__ . '/../view/commerce/index/index.phtml',
'error/404' => __DIR__ . '/../view/error/404.phtml',
'error/index' => __DIR__ . '/../view/error/index.phtml',
],
'template_path_stack' => [
__DIR__ . '/../view',
],
],
];
Where is the issue? zend-di is already installed.
Your error is caused in line
Controller\IndexController::class => InvokableFactory::class
this does not provide a "Commerce\Model\Commerce" to the constructor of your IndexController. You need to change this to provide the dependency:
'controllers' => [
'factories' => [
Controller\IndexController::class => function($container) {
return new Controller\IndexController(
$container->get(\Commerce\Model\Commerce::class)
);
},
],
],
'service_manager' => [
'factories' => [
\Commerce\Model\Commerce::class => function($sm) {
/* provide any dependencies if needed */
/* Create the model here. */
return new \Commerce\Model\Commerce($dependencies);
},
]
],
The best approach is to provide its own factory for your Commerce\Model\Commerce class, as seen above in the setting 'factories' of 'service_manager'.
EDIT: as request, if you want to do everything inside the controller Factory, here is a simple example:
'controllers' => [
'factories' => [
Controller\IndexController::class => function($container) {
$dbAdapter = $container->get('Zend\Db\Adapter\Adapter');
$resultSetPrototype = new ResultSet();
$tableGateway = new TableGateway('commerceTableName', $dbAdapter, null, $resultSetPrototype);
return new Controller\IndexController(
new \Commerce\Model\Commerce($tableGateway)
);
},
],
],

ZF3 - Set field in fieldset required depnding on form

I have a fieldset with a field called "company" which is not required by default. Now there is a form which adds this fieldset and now "company" should be required. Is there a way to do this in e. g. the form factory? Or can I rewrite the input filters of the fieldset in my inputfilter class binded to the form?
Thanks for any response!
Fieldset
class MyFieldset extends Fieldset implements InputFilterProviderInterface {
public function init() {
$this->add([
'name' => 'company',
'options' => [
'label' => 'Firma'
],
'attributes' => [
'id' => 'address-company'
]
]);
}
public function getInputFilterSpecification() {
return [
'company' => [
'required' => false,
'filters' => [
['name' => 'StripTags'],
['name' => 'StringTrim'],
['name' => 'ToNull']
],
'validators' => [
[
'name' => 'StringLength',
'options' => [
'encoding' => 'UTF-8',
'min' => 1,
'max' => 128
]
]
]
],
];
}
}
Form
class MyForm extends Form {
public function init() {
$this->add([
'type' => MyFieldset::class,
'name' => 'test',
'options' => [
'use_as_base_fieldset' => true
]
]);
}
}
Form Factory
class MyFormFactory implements FactoryInterface {
public function __invoke(ContainerInterface $container, $requestedName, array $options = null) {
$hydratorManager = $container->get('HydratorManager');
$inputFilterManager = $container->get('InputFilterManager');
$form = new MyForm();
$form->setHydrator($hydratorManager->get(DoctrineObject::class));
$form->setInputFilter($inputFilterManager->get(MyInputFilter::class));
$form->bind(new Entity());
return $form;
}
}
The only way I can think of doing it is.
In your fieldset use:
private $companyRequired = FALSE;
public function setCompanyRequired($companyRequired)
{
$this->companyRequired = (bool) $companyRequired;
}
public function getInputFilterSpecification()
{
return [
'company' => [
'required' => $this->companyRequired,
'filters' => [
['name' => 'StripTags'],
['name' => 'StringTrim'],
['name' => 'ToNull']
],
'validators' => [
[
'name' => 'StringLength',
'options' => [
'encoding' => 'UTF-8',
'min' => 1,
'max' => 128
]
]
]
],
];
}
in the form where the company is required place this in your init() method after adding the fieldset:
$this->get('test')->setCompanyRequired(TRUE);
As the getInputFilterSpecification() method is not read until you validate your form this should work.
I have previously answered a similar question relating to over loading the default input filter options of a nested fieldset.
You can do the same inside the form using the Zend\InputFilter\InputFilterProviderInterface
class MyForm extends Form implements InputFilterProviderInterface
{
public function init()
{
//..
}
public function getInputFilterSpecification()
{
return [
'test' => [
'type' => 'Zend\\InputFilter\\InputFilter',
'company' => [
'required' => true,
],
],
];
}
}
Or alternately, you can achieve the same by manually configuring the fieldset's input filter inside MyFormFactory;
class MyFormFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
//.. same code
$form->getInputFilter() // fetch the input filter you just attached.
->get('test') // the name of the neseted fieldset (another input filter)
->get('company') // the name of the input
->setRequired(true); // set required to true :)
return $form;
}
}

ZF3 unable to populate select field from db

Can anyone explain me the easiest way to populate select field in form from db?
ChartsForm.php
<pre>
<?php
namespace Charts\Form;
use Zend\Form\Form;
use Charts\Model\PostRepositoryInterface;
class ChartsForm extends Form
{
private $postRepository;
public function __construct(PostRepositoryInterface $postRepository)
{
$this->postRepository = $postRepository;
// We will ignore the name provided to the constructor
parent::__construct('album');
$this->add([
'name' => 'id',
'type' => 'hidden',
]);
$this->add([
'name' => 'title',
'type' => 'text',
'options' => [
'label' => 'Title',
],
]);
$this->add(array(
'type' => 'Radio',
'name' => 'select',
'options' => array(
'label' => 'select type',
'value_options' => array(
'1' => 'Index',
'2' => 'Security',
),
),
'attributes' => array(
'value' => '1'
)
));
$this->add(array(
'type' => 'Zend\Form\Element\Select',
'name' => 'gender',
'options' => array(
'value_options' => $this->postRepository->findAllPosts(),
),
'attributes' => array(
'value' => '1'
)
));
$this->add([
'name' => 'submit',
'type' => 'submit',
'attributes' => [
'value' => 'Go',
'id' => 'submitbutton',
],
]);
}
}
</pre>
module.config.php
<pre>
<?php
namespace Charts;
use Zend\ServiceManager\Factory\InvokableFactory;
use Zend\Db\Adapter\AdapterAbstractServiceFactory;
return [
'controllers' => [
'factories' => [
Model\ChartSqlRepository::class => Factory\ChartSqlRepositoryFactory::class,
],
],
'service_manager' => [
'aliases' => [
Model\PostRepositoryInterface::class => Model\PostRepository::class,
],
'factories' => [
Model\PostRepository::class => InvokableFactory::class,
],
],
'view_manager' => [
'template_path_stack' => [
'charts' => __DIR__ . '/../view',
],
],
'router' => [
'routes' => [
'charts' => [
'type' => 'segment',
'options' => [
'route' => '/charts[/:action[/:id]]',
'constraints' => [
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
],
'defaults' => [
'controller' => Controller\ChartsController::class,
'action' => 'index',
],
],
],
],
],
// 'service_manager' => [
// 'factories' => [
// 'Zend\Db\Adapter\Adapter' => AdapterAbstractServiceFactory::class,
// ],
// ],
];
</pre>
module.php
<pre>
<?php
namespace Charts;
use Zend\ModuleManager\Feature\ConfigProviderInterface;
use Zend\Db\Adapter\AdapterInterface;
use Zend\Db\ResultSet\ResultSet;
use Zend\Db\TableGateway\TableGateway;
class Module implements ConfigProviderInterface
{
public function getConfig()
{
return include __DIR__ . '/../config/module.config.php';
}
public function getServiceConfig()
{
return [
'factories' => [
Model\BKPagesTable::class => function($container) {
$tableGateway = $container->get(Model\BKPagesTableGateway::class);
return new Model\BKPagesTable($tableGateway);
},
Model\BKPagesTableGateway::class => function ($container) {
$dbAdapter = $container->get(AdapterInterface::class);
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Model\BKPages());
return new TableGateway('bkpages', $dbAdapter, null, $resultSetPrototype);
},
Model\BuyOrdersTable::class => function($container) {
$tableGateway = $container->get(Model\BuyOrdersTableGateway::class);
return new Model\BuyOrdersTable($tableGateway);
},
Model\BuyOrdersTableGateway::class => function($container) {
$dbAdapter2 = $container->get('api2web');
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Model\BuyOrders());
return new TableGateway('BuyOrders', $dbAdapter2, null, $resultSetPrototype);
},
//Stocklist Table
Model\StockListTable::class => function($container) {
$tableGateway = $container->get(Model\StockListTableGateway::class);
return new Model\StockListTable($tableGateway);
},
Model\StockListTableGateway::class => function($container) {
$dbAdapter2 = $container->get('api2web');
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Model\StockList());
return new TableGateway('StockList', $dbAdapter2, null, $resultSetPrototype);
},
//Fulldayquot Table
Model\FulldayquotTable::class => function($container) {
$tableGateway = $container->get(Model\FulldayquotTableGateway::class);
return new Model\FulldayquotTable($tableGateway);
},
Model\FulldayquotTableGateway::class => function($container) {
$dbAdapter2 = $container->get('api2web');
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Model\Fulldayquot());
return new TableGateway('fulldayquot', $dbAdapter2, null, $resultSetPrototype);
},
],
];
}
public function getControllerConfig()
{
return [
'factories' => [
Controller\ChartsController::class => function($container) {
return new Controller\ChartsController(
$container->get(Model\BKPagesTable::class)
);
},
],
];
}
}
</pre>
ChartSqlRepositoryFactory.php
<pre>
<?php
namespace Charts\Factory;
use Charts\Model\ChartSqlRepository;
use Zend\Db\Adapter\AdapterInterface;
use Blog\Model\PostRepositoryInterface;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
class ChartSqlRepositoryFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
return new ChartSqlRepository(
$container->get($container->get(PostRepositoryInterface::class)
);
}
}
</pre>
ChartSqlRepository.php
<pre>
<?php
namespace Charts\Model;
use InvalidArgumentException;
use RuntimeException;
use Zend\Db\Adapter\AdapterInterface;
use Zend\Db\Adapter\Driver\ResultInterface;
use Zend\Db\ResultSet\ResultSet;
class ChartSqlRepository implements PostRepositoryInterface
{
private $db;
private $repository;
public function __construct( ChartSqlRepository $repository)
{
$this->repository = $repository;
}
public function findAllPosts()
{
$sql = new Sql($this->db);
$select = $sql->select('BKPages');
$stmt = $sql->prepareStatementForSqlObject($select);
$result = $stmt->execute();
var_export($result);
die();
}
public function IndexesOptions()
{
$sql = new Sql($this->db);
$select = $sql->select('BKPages');
$stmt = $sql->prepareStatementForSqlObject($select);
$result = $stmt->execute();
$selectData = array();
foreach ($result as $res) {
$selectData[$res['MenuID']] = $res['MenuID'];
}
return $selectData;
}
public function findPost($id)
{
}
}
</pre>
I am trying to populate select field in ChartsForm.php from db and created a factory, maybe some error in module.config.php it's says.
Catchable fatal error: Argument 1 passed to Charts\Form\ChartsForm::__construct() must be an instance of Charts\Model\PostRepositoryInterface, none given, called in C:\wamp\www\bw\module\Charts\src\Controller\ChartsController.php on line 60 and defined in C:\wamp\www\bw\module\Charts\src\Form\ChartsForm.php on line 24
Help me out please.
Can you be more specific? What type of database?
You have two options: PDO (For all driver) and MySQLy (for MySQL).
Look here for a greater response:
https://stackoverflow.com/a/8255054/6784143
UPDATE:
You can connect your Sql Server with this line code (running good on .php).
resource mssql_connect ([ string $servername [, string $username [, string $password [, bool $new_link = false ]]]] )
You can execute a query with this line code.
mixed sqlsrv_query ( resource $conn , string $sql [, array $params [, array $options ]] )

Yii2 create custom rest action with post params

In my yii2 application i need to develop a rest action authUser()
public function actionAuthUser($username, $password)
{
return \api\models\User::findOne(['username' => $username, 'password' => sha1($password)]);
}
The params should be passed with post method because they contain sensitive informations.
How can i do this? How can i set the url manager to receive with post method my params?
Thanks in advance for all the help
[
'components' => [
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
// ...
[
'POST site/auth-user' => 'site/auth-user',
]
],
],
],
]
Or add behavior to Controller
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'auth-user' => ['post'],
],
],
];
}
And in controller action
public function actionAuthUser()
{
return \api\models\User::findOne(['username' => Yii::$app->request->post('username'), 'password' => sha1(Yii::$app->request->post('password'))]);
}