I don't understand Modules - zend-framework

I am trying to learn Zend Framework 3 and I have done the Album tutorial and another Blog tutorial. I've been writing straight php apps for about 3 years and wrote 2 web apps using Slim 3. I am trying to wrap my head around Modules. I don't quit understand what a module is. The way I understood Modules, is that a Module was a tiny app inside of the main app. I was trying to create 2 controllers in the same module and render different views from those controllers. I have searched all day and could not find a way to create two controllers and have them route to different views. But, I have found many examples of an app having 5 different modules all with only one controller and maybe different views that a class method would point to. Is it my understanding that a module should be created for every page? For example, a login module and an about module and a contact module?
module.config.php
<?php
namespace Blog;
use Zend\Router\Http\Literal;
use Zend\Router\Http\Segment;
use Zend\ServiceManager\Factory\InvokableFactory;
return [
// this line opens the configuration fro the Route Manager
'router' => [
// open configuration for all possible routes
'routes' => [
// Define a new route called 'blog'
'blog' => [
// define 'literal' route type:
'type' => Literal::class,
// configure the route itself
'options' => [
// listen to '/blog' as uri:
'route' => '/blog',
// define default controller and action to be called when
// this route is matched
'defaults' => [
'controller' => Controller\ListController::class,
'action' => 'index',
],
'login' => [
'type' => Literal::class,
'options' => [
'route' => '/blog/login',
'defaults' => [
'controller' => Controller\LoginController::class,
'action' => 'login',
],
],
],
],
],
],
],
'service_manager' => [
'aliases' => [
Model\PostRepositoryInterface::class => Model\PostRepository::class,
],
'factories' => [
Model\PostRepository::class => InvokableFactory::class,
],
],
'controllers' => [
'factories' => [
Controller\ListController::class => Factory\ListControllerFactory::class,
Controller\LoginController::class => InvokableFactory::class,
],
],
'view_manager' => [
'template_map' => [
'login/login' => __DIR__ . '/../view/blog/login/login.phtml'
],
'template_path_stack' => [
__DIR__ . '/../view'
],
]
];
file structure
|-- module
| |-- Application
| | |-- config
| | | |-- module.config.php
| | |-- src
| | | |-- Module.php
| | | |-- Controller
| | | |-- IndexController.php
| | |-- test
| | | |-- Controller
| | | |-- IndexControllerTest.php
| | |-- view
| | |-- application
| | | |-- index
| | | |-- index.phtml
| | |-- error
| | | |-- 404.phtml
| | | |-- index.phtml
| | |-- layout
| | |-- layout.phtml
| |-- Blog
| |-- config
| | |-- module.config.php
| |-- src
| | |-- Module.php
| | |-- Controller
| | | |-- ListController.php
| | | |-- LoginController.php
| | |-- Factory
| | | |-- ListControllerFactory.php
| | |-- Model
| | |-- Post.php
| | |-- PostCommandInterface.php
| | |-- PostRepository.php
| | |-- PostRepositoryInterface.php
| |-- view
| |-- blog
| |-- list
| | |-- index.phtml
| |-- login
| |-- login.phtml
I'm working in the Blog module. When I call the LoginController.php I want it to show the login.phtml. If I comment out the blog route it will work, but when I uncomment the blog route I get a 'The requested URL could not be matched by routing.' error.

Routing is not related to the template that is shown, so it sounds like you actually have a routing problem.
Your routing configuration is not structured correctly. It should look more like this:
return [
// this line opens the configuration for the Route Manager
'router' => [
// open configuration for all possible routes
'routes' => [
// Define a new route called 'blog'
'blog' => [
// define 'literal' route type:
'type' => Literal::class,
// configure the route itself
'options' => [
// listen to '/blog' as uri:
'route' => '/blog',
// define default controller and action to be called when
// this route is matched
'defaults' => [
'controller' => Controller\ListController::class,
'action' => 'index',
],
],
'may_terminate' => true,
'child_routes' => [
'login' => [
'type' => Literal::class,
'options' => [
'route' => '/login',
'defaults' => [
'controller' => Controller\LoginController::class,
'action' => 'login',
],
],
],
],
],
],
],
Differences:
The 'login' route should be a child route, not part of the blog route options.
I added 'may_terminate' => true which makes the /blog route work (routes with child routes are not accessible by default)
I changed 'route' => '/blog/login', to 'route' => '/login', in the login route. As it's a child of the blog you don't need to repeat the blog route's path.
Edit: if you want both routes at the top level instead:
return [
// this line opens the configuration for the Route Manager
'router' => [
// open configuration for all possible routes
'routes' => [
// Define a new route called 'blog'
'blog' => [
// define 'literal' route type:
'type' => Literal::class,
// configure the route itself
'options' => [
// listen to '/blog' as uri:
'route' => '/blog',
// define default controller and action to be called when
// this route is matched
'defaults' => [
'controller' => Controller\ListController::class,
'action' => 'index',
],
],
],
'login' => [
'type' => Literal::class,
'options' => [
'route' => '/login',
'defaults' => [
'controller' => Controller\LoginController::class,
'action' => 'login',
],
],
],
],
],
This adds routes for /blog and /login. If you want the login page to be at /blog/login you'll have to edit the path in the login route appropriately.

Related

Laravel custom user table and JWT authenticate

Try to implements a custom user class in Laravel JWT authentication
I'm trying to implement an application that use a custom table for the user's informations.
My table _user looks like that
'USR_NoRole',
'USR_CompleteName',
'USR_UserName',
'USR_Password',
'USR_BBlocked',
'USR_BAssociation',
'USR_NoPeriode',
'USR_NoEntite',
'USR_CreateUser',
'USR_CreateTimestamp',
'USR_UpdateUser',
'USR_UpdateTimestamp'
When i create a new user, everything works and i get back a token
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
'noRole' => 'required',
'completeName' => 'required',
'name' => 'required|string|max:255',
'password' => 'required|string|min:6|confirmed',
'noPeriode' => 'required',
'noEntite' => 'required'
]);
if ($validator->fails()) {
return response()->json($validator->errors()->toJson(), 400);
}
$user = User::create([
'USR_NoRole' => $request->get('noRole'),
'USR_CompleteName' => $request->get('completeName'),
'USR_UserName' => $request->get('name'),
'USR_Password' => Hash::make($request->get('password')),
'USR_BBlocked' => $request->get('isBlocked'),
'USR_BAssociation' => $request->get('isAssociation'),
'USR_NoPeriode' => $request->get('noPeriode'),
'USR_NoEntite' => $request->get('noEntite'),
'USR_CreateUser' => "jschneider",
'USR_CreateTimestamp' => date("Y-m-d H:i:s"),
'USR_UpdateUser' => "jschneider",
'USR_UpdateTimestamp' => date("Y-m-d H:i:s"),
]);
$token = JWTAuth::fromUser($user);
return response()->json(compact('user', 'token'), 201);
}
Here the result
{
"user": {
"USR_NoRole": "1",
"USR_CompleteName": "Complete Name",
"USR_UserName": "test#test.com",
"USR_Password": "$2y$10$bli2OTQQfsq7h4/XryZT4Op4p5DCnEGANCR5MYagHbndNU/ULmE3G",
"USR_BBlocked": null,
"USR_BAssociation": null,
"USR_NoPeriode": "1",
"USR_NoEntite": "1",
"USR_CreateUser": "test",
"USR_CreateTimestamp": "2019-04-01 07:02:13",
"USR_UpdateUser": "test",
"USR_UpdateTimestamp": "2019-04-01 07:02:13",
"id": 3
},
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9sb2NhbGhvc3RcL2xhcmF2ZWwtand0XC9wdWJsaWNcL2FwaVwvcmVnaXN0ZXIiLCJpYXQiOjE1NTQxMDIxMzQsImV4cCI6MTU1NDEwNTczNCwibmJmIjoxNTU0MTAyMTM0LCJqdGkiOiJ2UGFURmNVbFE2WHVSY251Iiwic3ViIjozLCJwcnYiOiI4N2UwYWYxZWY5ZmQxNTgxMmZkZWM5NzE1M2ExNGUwYjA0NzU0NmFhIn0.Vi75d8uVSpzR_VddgfhJVGcyaNd-MsvjazPuUy81RXg"
}
But after that, when i try to login with this new user, i can't get a token. The result is always :
{
"message": "SQLSTATE[42S22]: Column not found: 1054 Unknown column 'name' in 'where clause' (SQL: select * from `_user` where `name` = jschneider#patinfo.ch limit 1)",
}
How can i configure the credentials to say that the field name matches with USR_UserName and the field password matches with USR_Password ?
My custom user class
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Tymon\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject
{
use Notifiable;
protected $table = '_user';
public $timestamps = false;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'USR_NoRole', 'USR_CompleteName', 'USR_UserName', 'USR_Password', 'USR_BBlocked', 'USR_BAssociation', 'USR_NoPeriode', 'USR_NoEntite', 'USR_CreateUser', 'USR_CreateTimestamp', 'USR_UpdateUser', 'USR_UpdateTimestamp'
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'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();
}
public function getJWTCustomClaims()
{
return [];
}
}
the config/auth.php file
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option controls the default authentication "guard" and password
| reset options for your application. You may change these defaults
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider.
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| Supported: "session", "token"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| If you have multiple user tables or models you may configure multiple
| sources which represent each model / table. These sources may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| You may specify multiple password reset configurations if you have more
| than one user table or model in the application and you want to have
| separate password reset settings based on the specific user types.
|
| The expire time is the number of minutes that the reset token should be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
*/
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
],
],
];
Can anyone explain me how to set the app and create a token based on this 2 fields ?
place it on your controller
Config::set('jwt.user', 'App\OtherMOdel'); Config::set('auth.providers.users.model', \App\OtherMOdel::class);

How to set a default action for SocialEngine module

I have created a module with two controllers: Index and Pay. There are actions for both controllers, i.e.
Index -> indexAction
Pay -> indexAction, callbackAction, etc.
I have defined routes in the module's manifest.php, although it seems defining the routes in that file makes no difference as those routes work correctly anyway. The problem is when I browse the root of the module i.e. http://example.com/pgateway, only a specific action from my second controller (PayController->callbackAction) is executed. Why is that and how can I make say IndexController->indexAction the default page when example.com/pgateway is browsed?
My route definitions (manifest.php):
'routes' => [
'pay_general' => [
'route' => 'pgateway/:controller/:action/*',
'defaults' => [
'module' => 'pgateway',
'controller' => 'pay',
'action' => 'index',
],
'reqs' => [
'controller' => '\D+',
'action' => '\D+',
],
],
'pay_callback' => [
'route' => 'pgateway/:controller/:action/*',
'defaults' => [
'module' => 'pgateway',
'controller' => 'pay',
'action' => 'callback',
],
'reqs' => [
'controller' => '\D+',
'action' => '\D+',
],
],
],
route should be unique, in each definition. :action means it would work with values as well as empty. Incase of empty it will use defaults.
In your case latest route is overriding.
Try removing pay_callback, it would work as in pay_general.
Convention is manage one route for a controller, and manage accordingly.

datatype in request parameter $id = $this->params()->fromRoute('id');

so far I have worked only with integer values as request parameters in urls.
Now I need to work with strings which contain spaces etc. for example:
"xyz 666 888-VCT"
I send the route and parameters out of my view script:
PCB
I try to get it within my controller action:
$id = $this->params()->fromRoute('id');
How is the right way to catch a string value in this case?
edit:
here additional my routing paramters:
'pcb' => [
'type' => Segment::class,
'options' => [
'route' => '/pcb[/:action[/:id]]',
'constraints' => [
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[a-zA-Z][a-zA-Z0-9_-]*',
],
'defaults' => [
'controller' => Controller\PcbController::class,
'action' => 'index',
],
],
],
I get always
the requested url could not match by routing
My url looks like follows:
...pcb/index/3441 99901 B 03 2519 xyz
I'm sure the problem are the spaces, so my question a bit more detailed, can I quote or somewhat the routing parameter?
SOLVED:
my regular expression in the routing was wrong, I changed it to:
'id' => '[a-zA-Z0-9\-\. /]*',

Laravel 5 Cannot Find Driver Postgre SQL

I'm working on a Laravel Project which using a Postgre database, I run
php artisan migrate
Then I get error
[PDPException] could not find driver
But all my drivers is properly installled and enabled in my php.ini . I have a Yii Application that uses Postgre and works fine. php_pdo_pgsql is already installed and enabled.
Here's my config on Laravel
<?php
return [
/*
|--------------------------------------------------------------------------
| PDO Fetch Style
|--------------------------------------------------------------------------
|
| By default, database results will be returned as instances of the PHP
| stdClass object; however, you may desire to retrieve records in an
| array format for simplicity. Here you can tweak the fetch style.
|
*/
'fetch' => PDO::FETCH_CLASS,
/*
|--------------------------------------------------------------------------
| Default Database Connection Name
|--------------------------------------------------------------------------
|
| Here you may specify which of the database connections below you wish
| to use as your default connection for all database work. Of course
| you may use many connections at once using the Database library.
|
*/
'default' => 'pgsql',
/*
|--------------------------------------------------------------------------
| Database Connections
|--------------------------------------------------------------------------
|
| Here are each of the database connections setup for your application.
| Of course, examples of configuring each database platform that is
| supported by Laravel is shown below to make development simple.
|
|
| All database work in Laravel is done through the PHP PDO facilities
| so make sure you have the driver for your particular database of
| choice installed on your machine before you begin development.
|
*/
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'database' => storage_path('database.sqlite'),
'prefix' => '',
],
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
],
'pgsql' => [
'driver' => 'pgsql',
'host' => '192.168.3.9',
'database' => 'qms',
'username' => 'postgres',
'password' => 'postgres',
'charset' => 'utf8',
'prefix' => '',
'schema' => 'public',
],
'sqlsrv' => [
'driver' => 'sqlsrv',
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
],
],
/*
|--------------------------------------------------------------------------
| Migration Repository Table
|--------------------------------------------------------------------------
|
| This table keeps track of all the migrations that have already run for
| your application. Using this information, we can determine which of
| the migrations on disk haven't actually been run in the database.
|
*/
'migrations' => 'migrations',
/*
|--------------------------------------------------------------------------
| Redis Databases
|--------------------------------------------------------------------------
|
| Redis is an open source, fast, and advanced key-value store that also
| provides a richer set of commands than a typical key-value systems
| such as APC or Memcached. Laravel makes it easy to dig right in.
|
*/
'redis' => [
'cluster' => false,
'default' => [
'host' => '127.0.0.1',
'port' => 6379,
'database' => 0,
],
],
];
You will need the doctrine/dbal package to use postgres. https://packagist.org/packages/doctrine/dbal
Add this to your application by requiring the package:
composer require doctrine/dbal

Zend Framework 2 (ZF2) & Doctrine 2 with dynamic form using a ManyToMany association

What is the best practice for using forms to updating roles who have multiple permissions?
My roles table:
+----+-------+
| id | name |
+----+-------+
| 1 | admin |
| 2 | user |
+----+-------+
My permissions table:
+----+------------+
| id | name |
+----+------------+
| 1 | createUser |
| 2 | updateUser |
| 3 | createRole |
| 4 | updateRole |
+----+------------+
My ManyToMany association table, role_permission:
+---------+---------------+
| role_id | permission_id |
+---------+---------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 2 | 2 |
+---------+---------------+
My goal is to show all roles with a radiobutton and dynamically show the associated permissions with checkboxes.
Image of my problem:
image
When I click the admin role, the permissions aren't updated with the selected role (of course). This brings me back to my question: what is the best practice to dynamically show the right permissions for the selected role using ZF2 + Doctrine 2?
This is how I generate my form:
use Zend\Form\Form;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Doctrine\ORM\EntityManager;
class EditRoleForm extends Form implements InputFilterAwareInterface
{
private $editRoleInputFilter;
private $entityManager;
private $roleRepository;
private $permissionRepository;
public function __construct(EntityManager $entityManager) {
parent::__construct('editRole');
$this->editRoleInputFilter = new InputFilter();
$this->entityManager = $entityManager;
$this->roleRepository = $entityManager->getRepository('Authorization\Entity\Role');
$this->permissionRepository = $entityManager->getRepository('Authorization\Entity\Permission');
$this->add(array(
'name' => 'roles',
'type' => 'DoctrineModule\Form\Element\ObjectRadio',
'options' => array(
'label' => 'Roles',
'label_attributes' => array(
'class' => 'col-sm-12'
),
'object_manager' => $this->entityManager,
'target_class' => 'Authorization\Entity\Role',
'property' => 'name',
'is_method' => true,
'find_method' => array(
'name' => 'findAll',
'params' => array(
'orderBy' => array('id' => 'ASC'),
),
),
),
));
$this->add(array(
'name' => 'permissions',
'type' => 'DoctrineModule\Form\Element\ObjectMultiCheckbox',
'options' => array(
'label' => 'Permissions',
'label_attributes' => array(
'class' => 'col-sm-12'
),
'object_manager' => $this->entityManager,
'target_class' => 'Authorization\Entity\Permission',
'property' => 'name',
'is_method' => true,
'find_method' => array(
'name' => 'findAll',
'params' => array(
'orderBy' => array('id' => 'ASC'), // Sorting won't work
),
),
),
));
$this->add(array(
'name' => 'submit',
'type' => 'Submit',
'attributes' => array(
'value' => 'Update role',
'id' => 'submitEditRoleButton',
'class' => 'btn btn-white'
),
));
// assign input filters and validators
$this->editRoleInputFilter->add(array(
'name' => 'permissions',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
));
$this->setInputFilter($this->editRoleInputFilter);
}
}
As you can see, I don't know how to link the checkboxes to the selected radiobuttons.