I'm planning to use ZF2 in a future project, so I'm trying Zend Framework 2 RC1 now. I started with authentication step, and noticed that when i chose a different name than 'Zend_Auth' for session storage namespace, i can't access to object stored in session (AuthenticationService class' hasIdentity method returned false, despite User object data set in session).
<?php
namespace Application\Controller;
use Zend\Authentication\Adapter\DbTable as AuthAdapter;
use Zend\Authentication\AuthenticationService;
use Zend\Authentication\Storage\Session as SessionStorage;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Application\Model\User;
use Application\Form\LoginForm;
class LoginController extends AbstractActionController
{
public function indexAction()
{
$auth = new AuthenticationService();
if ($auth->hasIdentity()) {
return $this->redirect()->toRoute('application');
}
$form = new LoginForm();
return array('form' => $form);
}
public function loginAction()
{
$auth = new AuthenticationService();
$form = new LoginForm();
$form->get('submit')->setAttribute('value', 'Add');
$request = $this->getRequest();
if ($request->isPost()) {
$user = new User();
$form->setInputFilter($user->getInputFilter('login'));
$form->setData($request->getPost());
if ($form->isValid()) {
$data = $form->getData();
// Configure the instance with constructor parameters...
$sm = $this->getServiceLocator();
$dbAdapter = $sm->get('db-adapter');
$authAdapter = new AuthAdapter($dbAdapter, 'users', 'username', 'password');
$authAdapter
->setIdentity($data['username'])
->setCredential(sha1($data['password']));
// Use 'users' instead of 'Zend_Auth'
$auth->setStorage(new SessionStorage('users'));
$result = $auth->authenticate($authAdapter);
if ($result->isValid()) {
// store the identity as an object where only the username and
// real_name have been returned
$storage = $auth->getStorage();
// store the identity as an object where the password column has
// been omitted
$storage->write($authAdapter->getResultRowObject(
null,
'password'
));
// Redirect to list of application
return $this->redirect()->toRoute('application');
}
}
}
// processed if form is not valid
return array('form' => $form);
}
}
In this code, when i changed the below line,
$auth->setStorage(new SessionStorage('users'));
like this:
$auth->setStorage(new SessionStorage());
hasIdentity method returned true.
I checked two classes Zend\Authentication\AuthenticationService and Zend\Authentication\Storage\Session, and didn't see a way to access session data which has different session namespace other than default.
What i need to understand is how can i access session data which has a different namespace and if there is no way to do it for now, should we define this as a bug?
I can update the question if any other information needed.
We are kinda missing one part of your code, the one where you try and receive the user identity. im guessing that you have forgotten to pass the the SessionStorage Object with the same namespace.
Also the configuration of the Authentication object should be moved to a factory so these kind of issues to not arrise.
Thats my five cents atleast :)
Related
As the title suggests, I need to call a class.
Example config file within Laravel:
return [
'user' => App\User::class
];
The problem I have is that I need to do run the non-static method and not sure how to do that.
i.e. (new User())->getTable()
If you have this in your config file example_config_file.php:
return [
'user' => App\User::class,
];
You can call it like this using the new command to create an instance of the User model:
$userClass = config('example_config_file.user');
$user = new $userClass(); // blank instance of User model
Or like this if you want to use Eloquent methods:
$userClass = config('example_config_file.user');
$user = $userClass::find(1); // User model with id = 1
You can create a helper class and get any type of model like this:
class ModelHelper
{
public static function getModel($type, $id = null)
{
$modelClass = config("example_config_file.$type");
if($id) {
return $modelClass::find($id);
}
return new $modelClass();
}
}
Then in your code, whenever you need a user instance, call the helper class:
$user = ModelHelper::getModel('user'); // get a blank User model
$post = ModelHelper::getModel('post', 1); // get the post with id = 1
I'm trying to build a multi tenant app, where I have configured the database, the views folder, I know there must be some way out to configure the credentials of social app login for socialite. Well I tried few things to set it dynamically.
STEP 1
I created a class with the name of socialite in a separate folder and when the social login is called I'm implementing the following in my controller:
public function redirectSocialLogin()
{
$social = new SocialiteProvider();
$fb = $social->makeFacebookDriver();
return $fb->redirect();
}
and while callback I used following:
public function callbackSocialLogin($media)
{
$user = Socialite::driver($media)->user();
$data['name'] = $user->getName();
$data['email'] = $user->getEmail();
dd($data);
}
In my class I've following codes:
public function makeFacebookDriver()
{
$config['client_id'] = 'XXXXXXXXXXXXXXX';
$config['client_secret'] = 'XXXXXXXXXXXXXXX';
$config['redirect'] = 'http://XXXXXXXXXXXX/auth/facebook/callback';
return Socialite::buildProvider('\Laravel\Socialite\Two\FacebookProvider', $config);
}
It redirects perfectly to the social page but while getting a callback I'm getting an error, It again fetches the services.php file for configuration and doesn't get any.
STEP 2
I made a ServiceProvider under the name of SocialiteServiceProvider and extended the core SocialiteServiceProvider and placed the following codes:
protected function createFacebookDriver()
{
$config['client_id'] = 'XXXXXXXXXXXXXXX';
$config['client_secret'] = 'XXXXXXXXXXXXXXXXXXXX';
$config['redirect'] = 'http://XXXXXXXXXXX/auth/facebook/callback';
return $this->buildProvider(
'Laravel\Socialite\Two\FacebookProvider', $config
);
}
But again it throws back error which says driver is not setup. Help me out in this.
Thanks.
In your STEP 1, update the callback as below mentioned & try. $media is actually Request. So when initialising Socialite::driver($media) you are actually passing Request where you have to pass Facebook.
public function callbackSocialLogin(Request $request) {
$fbDriver = (new SocialiteProvider())->makeFacebookDriver();
$user = $fbDriver->user();
$data['name'] = $user->getName();
$data['email'] = $user->getEmail();
...
}
I'm using FosRestBundle and FosOauthServerBundle for an api with Symfony.
I have a route /login/username/password, in this action i loggin the user manually :
$encoder_service = $this->get('security.encoder_factory');
$encoder = $encoder_service->getEncoder($user);
$encoded_pass = $encoder->encodePassword($password, $user->getSalt());
if ($user->getPassword() == $encoded_pass) {
$token = new UsernamePasswordToken($user, $password, "api", $user->getRoles());
$this->get("security.context")->setToken($token);
$event = new InteractiveLoginEvent($this->get("request"), $token);
$this->get("event_dispatcher")->dispatch("security.interactive_login", $event);
}
If i check in my database after that (i save session in my database) i have a new line, and at the end of my action, if i check $this->getUser() i have the good one.
But after that, in another action, if i check $this->getUser() i have noting... I can't retrieve a user session from an action to another.
Do you have any ideas ?
Ediy: If i check $this->container->get('security.context')->getToken()i have :
{"roles":[{"role":"ROLE_USER"}],"authenticated":true,"attributes":[],"token":"MYTOKEN"}
But i haven't my user..
You should be able to fetch your user from security context. I usually define this as a separate function in the controllers where i need it :
public function getConnectedUser()
{
$user = null;
if ($this->get('session')->has('_security_member') && !is_null($this->get('security.context')->getToken())) {
$user = $this->get('security.context')->getToken()->getUser();
}
return $user;
}
You can then access it from every action of your controller.
$user = $this->getConnectedUser();
I have been wondering what is the right way to write code in OO style in model. Certainly you can have a function that retrieve data from DB and then map to model-level variables. This approach, however, becomes counterintuitive when you have other function in model trying to get other data from BD. For example:
class User extends CI_Model {
$id, $name, $age .... ;
public function get_user_from_db_with_id($id) {
...
// get data and map to variable.
}
public function get_all_users() {
// return all users in db
}
}
somewhere in controller:
$user = new User();
$ben = $user->get_user_from_db_with_id($id);
// this does not make sense!!
$all_user = $ben->get_all_users();
Any thought or comment?
Thanks in advance!
I had to make a similar decision and opted for this (trimmed for clarity)
class UserModel extends MY_Model
{
public $UserID = 0;
public $CustomerID = null;
public $FirstName = '';
public $LastName = '';
public $EmailAddress = '';
public $Password = null;
public $UserGroupID = true;
function __construct()
{
parent::__construct();
}
private function get($id)
{
$row = $this->get($id);
if ($row !== null)
{
$this->dbResultToObject($row, $this);
}
}
// Return an array of User objects
public function get_list($deleted = false, $userIDToInclude = null)
{
$params = array(null, $deleted, $userIDToInclude);
$query = $this->db->call("spUserGet", $params);
$users = array();
foreach ($query->result() as $row)
{
$user = new UserModel();
$this->dbResultToObject($row, $user);
$users[] = $user;
}
return $users;
}
// Other Methods (Validate, Save etc)
}
I use a mixture of public, protected and private properties so that the reflection code I've written to map the properties from the DB results and to the DB sproc calls then only includes the public properties and prevents too many parameters being sent. But that's getting off-topic so my controller then just looks like:
class Users extends MY_Controller
{
public function __construct()
{
parent::__construct();
$this->load->model('UserModel', 'user');
}
....
}
Then a list can be retrieved with
$users = $this->user->get_list();
And a single record with
$user = $this->user->get($userID);
i'm a big fan of thinking about the design in terms of "roles" - not resources. so a "User" is going to be able to get their own Profile. but its only going to be an "Admin" who is able to get All User Profiles.
so that distinction and important separation between what a User can do - get one record - and what an Admin can do - get all records - starts by having separate controllers for each. The User Controller methods are based upon verifying a single user and granting them access to one record. The Admin Controller methods are based upon verifying an admin and granting them access to all records. And this makes sense even from a URL design standpoint - you want your admin area clearly separate.
This separation at the controller makes everything simpler and easier. When you are resource oriented you are constantly checking the credentials in every method and even in your views. Views should be as simple as possible and not be tasked with "is this person an admin"? When you are role oriented - you check the credentials in the controller - and then your methods, the models, and the views are appropriate for what that 'role' needs to accomplish.
Since you are think about OO programming, i think you need to think: what does this class represent?
each instance means one user?
each instance means one user-data-generator?
if it's the first case, it makes sense this class has attributes like $id, $name, $age ....
and the following codes make sense
$user = new User();
$ben = $user->get_user_from_db_with_id($id);
if it's the second case, it shouldn't have the attributes like $id, $name, $age in you sample.
and these codes
$all_user = $ben->get_all_users();
should be replaced with this
$all_user = $user->get_all_users();
I found how to get a session container like this:
$session = new \Zend\Session\Container('base');
But what if I need to access the session in many places during processing a HTTP request.
Let's say in the Application module's indexAction in the IndexController, then I redirect it to the User\Controller\IndexController and need to access the session again, and then in a view helper or two, and who knows how often more.
When constructing the session container every time anew, that is a waste of processing time. Yes, I debugged it to see what's going on in the constructor, and yes, there is some code executed behind the scenes. It is not as if the constructor would just return a global variable or something else which would be immutable and doesn't need a construction process.
So what to do?
Should I create a service for it?
a controller plugin?
a view helper?
a service and a controller plugin and a view helper, with the latter calling the service?
I'm sure it is something that many people must have come across and have dealt with, but I can't find any information on this.
Any hint is dearly appreciated.
Many thanks in advance! :-)
Here's a more refined and improved version.
It consists of the service "SessionService", a ViewHelper (which calls the SessionService), a ControllerPlugin (which also calls the SessionService), and shows how to set them up in the configuration file "module.config.php".
Make sure you set "use" paths or use absolute class paths in config.
SessionService.php:
class SessionService
{
protected $sessionContainer;
public function setSessionContainer(
$sessionContainer
) {
$this->sessionContainer = $sessionContainer;
}
public function __invoke() {
return $this->sessionContainer;
}
}
SessionHelper.php:
class SessionHelper extends \Zend\View\Helper\AbstractHelper
{
protected $sessionService;
public function setSessionService(
$sessionService
) {
$this->sessionService = $sessionService;
}
public function __invoke() {
return $this->sessionService;
}
}
SessionPlugin.php:
class SessionPlugin extends AbstractPlugin
{
protected $sessionService;
public function setSessionService(
$sessionService
) {
$this->sessionService = $sessionService;
}
public function __invoke() {
return $this->sessionService;
}
}
module.config.php:
'service_manager' => array(
'factories' => array(
'sessionService' => function(
ServiceLocatorInterface $serviceLocator
) {
$sessionContainer = new \Zend\Session\Container('base');
$sessionService = new SessionService();
$sessionService->setSessionContainer($sessionContainer);
return $sessionService;
},
),
),
'controller_plugins' => array(
'factories' => array(
'sessionPlugin' => function(
AbstractPluginManager $pluginManager
) {
$sessionService = $pluginManager->getServiceLocator()->get('sessionService');
$sessionPlugin = new SessionPlugin();
$sessionPlugin->setSessionService($sessionService);
return $sessionPlugin;
},
),
),
'view_helpers' => array(
'factories' => array(
'sessionHelper' => function (
AbstractPluginManager $helperPluginManager
) {
$sessionService = $helperPluginManager->getServiceLocator()->get('sessionService');
$sessionHelper = new SessionHelper();
$sessionHelper->setSessionService($sessionService);
return $sessionHelper;
},
),
),
In your Controller write:-
use Zend\Session\Container;
Make Session variable
$user_session = new Container('user');
'user' is Your Session Name To put Value in Your Session write:
$user_session->username = 'xyz';
After Storing You can Access Your Session By:
$user_session-> username
To destroy Session Variable Use:
$session = new Container('user');
$session->getManager()->getStorage()->clear('user');
it is Just Like : -
unset($_SESSION['user']);
http://wownewcode.blogspot.in/2013/12/set-session-in-zend-framework-2.html
Once you've initialized a session Container you can just re-use it with $_SESSION['container_name'];
Basically $session = new \Zend\Session\Container('base'); will create an ArrayObject named base inside $_SESSION. One advantage of initializing by creating a Container is that you can specify a TTL or expiration after x-hops.
$_SESSION['base']['key'] = "store this value";
I think you need to use session Manager and service with the container storage to achieve your goal.
you can set it in your application Module
Application\Module.php
use Zend\Session\Config\SessionConfig;
public function onBootstrap(EventInterface $e)
{
//Your other code here
//configure session
$sessionConfig = new SessionConfig();
$sessionConfig->setOptions($config['session']);
}
and in module.config.php
'session' => array(
'save_path' => realpath(ZF2PROJECT_ROOT . '/data/session'),
'name' => 'ZF2PROJECT_SESSION',
),
and in your services you can use like this. Forexample in Authentication service.
class AuthenticationService
{
protected $storage = null;
public function getStorage()
{
if (null === $this->storage) {
$this->setStorage(new Storage\Session());
}
return $this->storage;
}
}
Here is my current provisional solution or workaround, consisting of:
- a service for storing the session container object.
- a controller plugin for easy access to the session container, without having to inject a dependency for it in every controller.
The session service:
class Session
{
private static $container;
public function getContainer() {
if (!isset(self::$container)) {
self::$container = new \Zend\Session\Container('base');
}
return self::$container;
}
public function __invoke() {
return $this->getContainer();
}
}
The controller plugin:
class Session extends AbstractPlugin
{
protected $sessionService;
public function __construct(
SessionService $sessionService
) {
$this->sessionService = $sessionService;
}
public function getContainer() {
return $this->sessionService->getContainer();
}
public function __invoke() {
return $this->getContainer();
}
}
Configuration in module.config.php:
'service_manager' => array(
'factories' => array(
'sessionService' => function($sm) {
return new Application\Service\Session\Session();
},
),
),
'controller_plugins' => array(
'factories' => array(
'session' => function($serviceLocator) {
$sessionService = $serviceLocator->get('sessionService');
return new Application\Service\Mvc\Controller\Plugin\Session($sessionService);
},
),
),
Usage example in any controller or controller plugin method:
$sessionContainer = $this->session->getContainer();
or short form (because session service and controller plugin both implement __invoke):
$sessionContainer = $this->session();
and then can use the session container to store any variables in it, like this:
$sessionContainer->foo = 'bar';
Because the session service is created by a factory function through module.config.php, it is only created once.
The actual session container is a static variable in the session service and only created once, i.e. if it doesn't exist.
In subsequent calls to the getSessionContainer function, this only once created static session container is returned.
This is just a provisional workaround solution, works for me for now, but for making it re-usable for other applications also, it should provide functions to customize the session container name and the storage place and strategy, those parts are missing in this simple workaround solution.
Note: A view helper should not be necessary for it. Session variables should not be set in a view, and if a view needs read access to them, the data should be passed via a view model from controller to view.