I have a Cake website and it needs to have two separate logins, each one will have their own login form and see different pages, it would be nice to have two different tables because there are no similarities between the two types of people.
Each login form will only be used by certain people and they will never login to the other form, and vice versa.
Also, the two login tables have a relationship between them, which requires 2 tables?
Is this possible?
First, add a couple of empty custom authenticate objects. We'll reuse the same logic that FormAuthenticate uses (that is, uses POST data to check the database for a user), but simply change the model within the object settings (later).
app/Controller/Component/Auth/ModelOneAuthenticate.php
<?php
App::uses('FormAuthenticate', 'Controller/Component/Auth');
class ModelOneAuthenticate extends FormAuthenticate {
}
app/Controller/Component/Auth/ModelTwoAuthenticate.php
<?php
App::uses('FormAuthenticate', 'Controller/Component/Auth');
class ModelTwoAuthenticate extends FormAuthenticate {
}
Then tell your app to use these objects to authenticate, and tell it what model to use. You can also customize the fields here. In your AppController:
public $components = array(
'Auth' => array(
'authenticate' => array(
'ModelOne' => array(
'userModel' => 'ModelOne',
'fields' => array(
'username' => 'my_custom_username_field',
'password' => 'some_password_field'
)
),
'ModelTwo' => array(
'userModel' => 'ModelTwo'
)
)
)
);
The first authentication object would check the model_ones table for a username in my_custom_username_field and password in some_password_field, while the second one would check model_twos using the standard username and password fields.
The simplest way to do this is to just set a different session key for each login type:
if ($loginTypeOne) {
$this->Auth->authenticate = array(
'Form'=> array(
'userModel'=> 'TypeOne',
)
);
AuthComponent::$sessionKey = 'Auth.TypeOne';
} else {
$this->Auth->authenticate = array(
'Form'=> array(
'userModel'=> 'TypeTwo',
)
);
AuthComponent::$sessionKey = 'Auth.TypeTwo';
}
When they have to login there is a similarity: Both will require it to enter credentials, usually an username/email and password. So a users table and a foo_profiles table and a bar_profiles table depending on the user type should work also.
If you really want to go with two total different tables and the MVC stack for them, then simply use two different controllers FooUsers and BarUsers and inside of each create a customized login method.
I have done this previously by writing custom Authentication components that extend from BaseAuthenticate. As long as they implement the authenticate() method then you'll be able to do whatever you want to each of the different types of user.
In your AppController you need to register the different components by doing something like
public $components = array(
"Session",
"Auth" => array(
'authenticate' => array("UserType1", "UserType2"),
)
);
Check out the cookbook for the rest of it.
You can have a look on this.
Define Model for both login member and then define table which you want to use for the user.
set variable in model.
class SearchedCategory extends AppModel {
var $name = 'SearchedCategory';
Var useTable = 'give your table name here.';
var $primaryKey = 'id';
}
Related
I'm facing trouble telling cake the most simple associations.
I have two Models:
CoreUser.php
CoreRole.php
.
One User has one Role.
How to assign that in cake? (HasOne or BelongsTo? => When to choose what?)
What to put in what Model? I tried both and ever end up with a recursion-error or it is just not working.
My SQL-Tables:
(tbl) core_users [id,username,password,role_id]
(tbl) core_roles [id,name]
My Models:
class CoreUser extends AppModel {
public $hasOne = array(
'Role' => array(
'className' => 'CoreRole',
'foreignKey' => 'id'
)
);
}
class CoreRole extends AppModel {
public $belongsTo = array(
'User' => array(
'className' => 'CoreUser',
'foreignKey' => 'role_id'
)
);
}
=> Can you give me the correct code i need to insert into one of both (or both) models to tell cake about the relationship?
Thanks in advance
Theoretically, there are two things you need to think about, one of them being non-Cakephp specific.
A. Relationships: A relationship is always bidirectional. When determining the relationship between two Models/Objects/Tables, you always ask two questions:
How many instances of B are related to one instance of A?
How many instances of A are related to one instance of B?
You've said, One user has one Role. However, One Role has how many Users related to it? That will tell you the complete relationship between a User and a Role. (Apologies for the digression but this is important and I'm referencing this book.)
B. Difference between hasMany and belongsTo:
This is determined based on the direction of traversing a relationship.
Based on point A, say you've determined that:
One User has one Role but One Role has many Users.
Now when you are in the User's model trying to fetch related Role data, you need to define the following in the User model:
class CoreUser extends AppModel {
public $belongsTo = array(
'Role' => array(
'className' => 'CoreRole',
'foreignKey' => 'role_id'
)
);
}
But when you are in the Role's model and trying to fetch related User data, you will need to define the following in the Role model:
class CoreRole extends AppModel {
public $hasMany = array(
'User' => array(
'className' => 'CoreUser',
'foreignKey' => 'role_id'
)
);
}
For a full discussion refer to this answer.
I have three models: User Image Group.
User belongs to an image;
Group belongs to a User (admin)
Group Has and belongs to user;
User has and belongs to group;
User and group model are connected with users_groups table;
When I am viewing a group there is listing the members (Users, this works) I need to extract them images what should I do?
You should use the ContainableBehavior. It will allow you to control what data you pull.
First, add it to your AppModel. I also always set $recursive = -1 so extra data is never pulled. Be careful with doing this with an existing app that relied on $recursive data.
class AppModel extends Model {
public $recursive = -1;
public $actsAs = array('Containable');
}
Then, during a find call, use the 'contain' query key. For example, on your GroupsController:
public function view($id = null) {
$results = $this->Group->find('first', array(
'conditions' => array(
'Group.id' => $id
),
'contain' => array(
'User' => array(
'Image'
)
)
));
}
This will bring in the User information associated with the group, along with the Image information associated with the users.
So I have a pretty typical user model, nothing special about it. I'd like to be able to use this model as a guest user when no other user is logged in.
Is there a way to create a guest account, or some sort of default when no account is set, in CakePHP so that Auth contains that?
So, for example, the id would be set as 0, username would be set to guest, etc.
Thank you,
James
You could override the find method at your User model, so that when Auth calls it (internally), you can intercept that and return whatever your want instead. Something like this (on your User model):
public function find($conditions, $fields, $order=null, $recursive=false) {
$user = parent::find($conditions, $fields, $order, $recursive);
if(empty($user)) {
// No "real" user found, let's create "guest"
$user = array(
'User' => array(
'id' => 0,
'username' => 'guest'
// add other fields as needed
)
);
}
return $user;
}
Just add it to the session.
$this->Session->write(AuthComponent::$sessionKey, array(
'User' => array(
'id' => 0,
'username' => 'guest'
)
));
In AppController :
public function beforeFilter() {
if(!$this->Auth->loggedIn()){
$this->loadModel('User');
$user = $this->User->findById(0); //Guest user id
$this->Auth->login($user['User']);
}
}
This works fine for me with CakePHP 2.2.
It allows permission configuration including guest's permission via a web interface : http://www.alaxos.net/blaxos/pages/view/plugin_acl_2.0
In Cake 2.0.5 when logging in using the Auth component, it would seem Cake is retrieving all related models; and with many associations, logging in takes a long time.
This problem was first identified here in this ticket but the "solution" given doesn't mean a lot, and I can't find anything else in the documentation.
Using the FormAuthenticate class in 2.0 you can subclass and add
whatever recursive level you feel is appropriate fairly easily.
Has anyone experienced this, and have a fix?
Below - sample code:
Standard login method:
public function login() {
$this->User->recursive = -1; // does nothing
if ($this->Auth->login()) {
$this->redirect($this->Auth->redirect());
} else {
$this->Session->setFlash('Invalid username or password.');
}
}
And the query cake is producing for my app:
SELECT `User`.`id`, `User`.`username`, `User`.`password`, `User`.`role`, `User`.`created`, `User`.`modified`, `Band`.`id`, `Band`.`name`, `Band`.`genre`, `Band`.`location`, `Band`.`influences`, `Band`.`founded`, `Band`.`bio`, `Band`.`created`, `Band`.`modified`, `Band`.`status`, `Band`.`website`, `Band`.`email`, `Band`.`contact_number`, `Band`.`user_id`, `Member`.`id`, `Member`.`user_id`, `Member`.`first_name`, `Member`.`last_name`, `Member`.`display_name`, `Member`.`dob`, `Member`.`gender`, `Member`.`bio`, `Member`.`influences`, `Member`.`band_id` FROM `users` AS `User` LEFT JOIN `bands` AS `Band` ON (`Band`.`user_id` = `User`.`id`) LEFT JOIN `members` AS `Member` ON (`Member`.`user_id` = `User`.`id`) WHERE `User`.`username` = 'admin' AND `User`.`password` = 'dcec839a9258631138974cbccd81219f1d5dfcfa' LIMIT 1
As you can see it's retrieving every field, and joining every model. My app only has 2 additional associations, but you can see how this might be an issue with very complex apps.
When really, it should just be the users table. Setting recursive appears to do absolutely nothing.
You can use recursive option for Auth component.
public $components = array(
'Auth' => array(
'authenticate' => array(
'Form' => array('recursive' => -1)
)
)
or in beforeFilter method:
$this->Auth->authenticate = array('Form' => array('recursive' => -1));
What Mark is suggesting is to extend the FormAuthenticate class, or essentially override it.
Create a new file app/Controller/Component/Auth/ExtendedFormAuthenticate.php
This is the basic structure of the code - I've left in the important bit where the recursive level is set in the _findUser method:
App::uses('FormAuthenticate', 'Controller/Component/Auth');
class ExtendedFormAuthenticate extends FormAuthenticate
{
public function authenicate(CakeRequest $request, CakeResponse $response) {
// foo
}
protected function _findUser($username, $password)
{
// bar
$result = ClassRegistry::init($userModel)->find('first', array(
'conditions' => $conditions,
'recursive' => -1
));
// fooBar
}
}
I've created a Gist with the whole lot in: https://gist.github.com/1565672
Oh, almost forgot, you'll need to setup the AuthComponent to use the extended class.
public $components = array(
'Auth'=> array(
'authenticate' => array(
'ExtendedForm'
)
),
);
What about using Containable and override find at model?
Modifying Containable fields required in beforeFind callback?
CakePHP's AuthComponent assumes you have a Users table that contains a username and password. I'd like to find a way to override the default tablename from Users to Accounts.
Background Information:
The way I have designed my database is to have a Users table and an Accounts table.
Accounts:
id
user_id
username
password
authentication service (for example, my site, Facebook, Google, OpenID, etc.)
Users:
simply has all the personal information of the user (age, gender, etc.)
The reason for this is so that
each user can have multiple accounts they can login from so they are not locked into one
I can connect the third-party services to an account for more awesomeness
Now back to the problem....
CakePHP has documentation on changing the default field name, but I can't find anything on changing the default table name, but assume it would be similar in nature...
Example of changing the default field name:
function beforeFilter() {
$this->Auth->fields = array(
'username' => 'username',
'password' => 'secretword'
);
}
Is there a way to accomplish this or should I restructure the tables keeping with CakePHP convention and still accomplish the same thing?
In app_controller.php:
function beforeFilter() {
$this->Auth->userModel = 'Account';
}
Below code was helpful in my case as accepting username in email field, you can also define password hash in options.
$this->Auth->authenticate = array(
'Basic' => array('userModel' => 'Account'),
'Form' => array(
'fields' => array('username' => 'email'),
'userModel' => 'Account'
)
);