I have read the tutorials on how to make a sub class of CWebUser and followed the instructions. The paths all work, and the code is getting into the right methods, however the value returned from my getter is always nil.
class PersonUser extends CWebUser {
// Store model to not repeat query.
private $_person;
// Load user model.
public function loadPerson($id=null, $duration=0)
{
$this->login($id,$duration);
$this->_person=Person::model()->findByPk(Yii::app()->user->id);
}
public function getPerson()
{
return $this->_person;
//return Person::model()->findByPk($this->id);
}
}
If I echo in the loadPerson method $this->_person->first_name after I set _person I get the value I expect. However, at any later time, if I ask for Yii::app()->user->person, the getPerson() method gets called, but $this->_person is now null. I know it's getting in there, if I uncomment the line below and have it look up the person every time, it works.
Is this an issue with Yii? I would really like to be able to cache the person object so I can reference it throughout the session without having to make more calls to the database. What am I missing??
There is no issue with Yii....
As per the documentation, CWebUser class identifies predefined variables "id" and "name" which remains persistent through out the session. Any additional variables should be used with getState() and setState() methods.
" Moreover CWebUser should be used together with IUserIdentity Class which implements the actual authentication algorithm. "
The method loadUser() is never called. And the login() call inside also doesn't make sense. A simpler implementation of getPerson() would be.
private $_person = false;
public function getPerson()
{
if($this->_person===false)
$this->_person = Person::model()->findByPk($this->id);
return $this->_person;
}
Related
I'm writing a helper function to check whether any address information is present in my database by checking if any of the relevant fields don't equal null.
So, I have two models which I need to accept in the function as an argument.
App\Customer
App\Supplier
The variable passed can be either one.
For a function that should only accept one kind of model, I can just do this:
function check_for_address_info(App\Customer $customer) {
// other unrelated stuff
}
Is there a way to accept both models, or do I have to manually check it in the function by doing something like this:
function check_for_address_info($param) {
if(!is_a($param, 'App\Customer' || !is_a($param, 'App\Supplier')) {
// not an instance of either model
return false;
}
// do stuff as normal
}
Any ideas on how to accept two different models as a function argument?
I'm on Laravel 5.8.
There is two approaches, if it makes sense inheritance wise, you can extend a parent model and declare it as the type of the parameter. This will be checked on run time and provide an error if you pass the wrong type to the method.
public class Profile extends Model {
}
public class Customer extends Profile {
}
public class Supplier extends Profile {
}
function check_for_address_info(App\Profile $customerOrSupplier) {
}
In weak typed languages, it is common to have parameters that are generic. The way PHP solve this problem, is you can declare it in PHP Doc blocks. This will not check types on run time and is mostly for typehinting, documentation and static analysis tools.
/**
* #parameter \App\Customer|\App\Supplier $customerOrSupplier
**/
function check_for_address_info($customerOrSupplier) {
}
I just stumbled over a PHP class and wonder if there was a valid reason for the way one of it's methods is written.
LogUtility::getLogger() is called as a static method in various other PHP classes of the PHP application. Does the used if statement make sense or is $logManager always null when getLogger() is called?
class LogUtility
{
/**
* #var LogManager
*/
protected static $logManager;
/**
* #return Logger
*/
public static function getLogger($name)
{
if (!self::$logManager) {
self::$logManager = GeneralUtility::makeInstance(LogManager::class);
}
return self::$logManager->getLogger($name);
}
}
You could quickly whip up a test, like below, and test / prove it yourself:
class someClass {
protected static $stored;
public static function test() {
echo '<br>Stored state:' . self::$stored;
if ( ! self::$stored) {
self::$stored = "set";
}
}
}
someClass::test();
someClass::test();
Output is:
Stored state:
Stored state:set
So, based on this simple test, the answer is Yes, the if statement makes sense. The static variable is set and maintained.
$logManager is set to a property of the class, so for now, its pretty much an empty class, just returning with a getter, the instance. This just sets the code to just reuse the object. So a bit of recycling code there.
In the class you are now able to play with this object freely. So if you run this LogUtility from another piece of code by setting it to a var, you have already instantiated it. e.g. $util = new LogUtility();now if someone comes along and tries to instantiate it again, $anotherUtil=new LogUtility(); this recycles the class, passing back the already instantiated instance, instead of instantiating a new one.
Therefore, yes, it kept it. Although, the var doesn't contain the "same value" per say, it contains a reference to the "same" class that was instantiated with he other variable, so it ends up a copy of the same instance there.
It will be null for only the first call in a lifecycle. This implements a design pattern called Singleton.
Check out https://sourcemaking.com/design_patterns/singleton/php/1
For instance i have class say that runs MySQL query and i want to encapsulate this class.
class SqlQuery {
private $database_connection_obj;
public function __construct($dbh) {
$this->$database_connection_obj = $dbh;
}
public function runQuery($query, $params) {
//run query implementation...
}
}
Encapsulation and information hiding means that i want to close direct access to class methods so function runQuery() should not be public so i make it private and add method exacuteQuery() with sole purpose to pass data to private function runQuery().
What is the practical use of doing so, because at the end it works exact same as code above.
Should there be some sanitation of input data done in public method before its passed to private method or why write this extra code at all?
class SqlQuery {
private $database_connection_obj;
public function __construct($dbh) {
$this->$database_connection_obj = $dbh;
}
public function exacuteQuery($external_query, $external_params) {
$this->runQuery($external_query, $external_params);
}
private function runQuery($query, $params) {
//run query implementation...
}
}
The main idea is to make classes look like black boxes, meaning that an outsider (namely, another class or function holding an instance of the class) has no idea how it works internally. It's abstract to them.
The internal workings of the query are of no importance to the class holding the query and calling ->run(), it only cares about the query running, not necessarily how or where it runs.
Note that a query specifically is quite the low level abstraction, it's relatively close to making direct calls to standard library functions/objects. But given that low level abstraction, you can make higher level abstraction.
For example, a UserStore, that internally uses SqlQuery objects to get and set User objects into the database. A class using UserStore isn't aware that UserStore is using SqlQuery objects internally, it just knows that it's an object that can save and retrieve User objects.
This sort of "hiding away" and "encapsulating" actually gives you a lot of power, because now classes can use other classes without depending on specific implementation details. If tomorrow you'd like to change the way you store Users, you just make a change to the UserStore class, as long as it has the same public API, the rest of your application wouldn't even be aware that something changed.
Given details has no difference, but it depends upon work. I use it much times when i have to make a function which cannot be access directly but only by the method for its use. It optimize the code and deny access outside the code. Otherwise any one can alter the parameters which can be harmful.
Our development team is debating a general best practice:
Is it better to access a session variable directly from a function in a model class or pass the session variable from the controller as an argument to a function in the model class. Look at the two examples below:
Accessing session variable directly from the model class to use in a query:
class MyModel {
public function getUserPrefs($userID) {
$this->query("SELECT * FROM my_table WHERE id=$_SESSION['userID']");
}
}
Or pass the session variable from the controller to a function in the model class as a function argument:
class MyController {
public function displayUsers() {
$this->model->getUserPrefs($_SESSION['userID']);
}
}
class MyModel {
public function getUserPrefs($userID) {
$this->query("SELECT * FROM my_table WHERE id=$userID");
}
}
The reasoning for passing it to the model from the controller is so all data that is referenced is coming from one point of entry, that being the controller.
What is recognized as a better practice?
The second version (passing $_SESSION['userId'] as an argument to the method) results in a more decoupled class, and therefore more flexible. Go with it.
You NEVER want to have session variables in your model. You should always pass these variables as parameters to the function in the model. This also makes your code more expandable and flexible. Consider a model that gets a user by their id. You may write a function like:
function find_by_id() {
// SELECT * FROM Users WHERE user_id = $_SESSION['user_id'];
}
However, what if you now what to build an admin functionality with a user lookup feature? Your model is hardcoded to use the session's user_id, but you want to be able to pass your own id. You would be better off:
function find_by_id($id) {
// SELECT * FROM Users WHERE user_id = $id
}
and in your controller
$user = Model::find_by_id(1);
//or
$user = Model::find_by_id($_SESSION['user_id']);
//etc
In this case, however, I would really consider making your code even MORE flexible:
function find($ids) {
// this is pseudo code, but you get the idea
if(is_array($ids))
$ids = implode(',', $ids); // if an array of ids was passed, implode them with commas
SELECT * FROM Users WHERE user_id IN ($ids);
}
This allows you to get multiple users in ONE query! Which is way more efficient. Then, in your view:
foreach($users as $user){
// iterate over each user and do stuff
}
You should also consider using a singelton class for a User to limit database load. Create a non-changing instance class called CurrentUser (for example) like:
class CurrentUser {
private static $user;
// we never instantiate it -its singleton
private function __construct() {}
public function user() {
return self::$user;
}
}
This is a really basic example of a singleton class and is missing a lot of stuff. If you want to know more about singleton classes, post another question.
Keep in mind that "session" is just yet another model. However first approach is unacceptable - what if you would like to fetch another users preferences just to compare them with something? Use the second approach.
I agree with Seth re. "You should also consider using a singelton class for a User to limit database load. Create a non-changing instance class called CurrentUser".
In my pseudo-MVC app I have class User (meaning current user) with methods for session, getting user info, roles etc., and class Member (meaning any given user) with methods for registering new users, getting/updating their properties etc but nothing to do with sessions, for example. Also, it is a singleton scenario, so current user is static and does not require much interaction with the DB.
So in my case, my controller and views make calls to User methods, e.g.
User::getId() or User::getGroups().
I have a session class that basicly just sets and retrieves session variables,
the reason I made it was so I could easily change it to use sessions or something
like memcache to set the items and have them accessible on multiple pages without hitting the database
I then have this user class which uses the session object to get session variables in it.
I am wanting to add to this user class though, to make it more encapsulated I would like to be able to set the variables that I am retrieving in this class
so right now I can display the userid with $user->userid; I would like to first have a method or something that sets its value from the session object I guess
Does this sound lke a good idea or possibly a lot of overhead?
And if what I am trying to do is a good idea maybe you could suggest/show example of how I should do it? I am thinking that if I add that method in that possibly I should move the code in the __construct method into it's own method
Basicly, I have the variables listed in the top part of the class that are used in the construct method, if I have multiple methods in the class though would I need to set them all at the top like that?
<?PHP
//user.class.php file
class User
{
public $userid;
public $name;
public $pic_url;
public $gender;
public $user_role;
public $location_lat;
public $location_long;
public $newuser;
function __construct()
{
global $session;
if($session->get('auto_id') != ''){
//set user vars on every page load
$this->userid = $session->get('auto_id'); //user id number
$this->name = $session->get('disp_name');
$this->pic_url = $session->get('pic_url');
$this->gender = $session->get('gender');
$this->user_role = $session->get('user_role');
$this->location_lat = $session->get('lat');
$this->location_long = $session->get('long');
$this->newuser = $session->get('newregister');
}else{
return false;
}
}
}
//with the class above I can easily show some user variables I have saved into a session like this below
$user = new user();
$user->userid;
?>
In general your idea is a good one
3 things I would do differently:
1) In your implementation doesn't seem to consider having several users. ie Several instances of the same class.
2) I would use factories instead of using IF in the constructor.
So for a user you have saved in the session you would call:
$savedUser = User::fromSession($userId);
for a new user
$user = new User()
3) Use the serialize and unserialze functions to save that data to the session
Then your class could could be implemented as
public static function fromSession($userId) {
return unserialize($session->get('users_'.$userId));
}
public function save() {
return $session->set('users_'.$this->id , serialize($this));
}
I guess this is vaguely an answer to the "is this a good idea" question. In my understanding, locating variables in the session versus refreshing them from the database is a question of the trade off between complex queries and deserializing data. The session data isn't a free magic cache that escapes database calls, it is just a convenient wrapper around a database call that you don't have to deal with. Any variable that you place in the session must be serializable. The whole collection of serialized data is then managed; the server fetches the data using the session key, deserializes it all, and hands it to the php script. Then when it closes the session for that request-response cycle it serializes it all and puts it back in the db.
So the mess in dealing with all that can, in some cases, be worse than the mess of just opening a connection and asking the db for the same stuff (or a subset of stuff) directly.
I would say that putting one or two key values in the session is a good stopping place, and relying on it too heavily for statefulness is a less-optimal plan.
I would set a new session with a name like "ValuesInSession" to true or false depending on whether or not you have session values for the fields in your user class. Then, in the sessions\users class you can check whether this session is true or false and set your values accordingly (IE from the existing sessions or to empty strings\0)
EDIT: You could, alternatively to putting that code in the user or sessions class, write a new class which could work with your users class to set the values properly (perhaps it could extend the sessions class?)
I'm not sure I understand the question, however, if you are using php 5, you can use the __set magic method to help with this.
Modifying your current class:
class User
{
private $id;
private $data = array();
public function __construct()
{
global $session;
$this->id = $session->get('auto_id');
$this->data = array(
'disp_name'=>$session->get('disp_name'),
'pic_url'=>$session->get('pic_url'),
'gender'=>$session->get('gender'),
'user_role'=>$session->get('user_role'),
'lat'=>$session->get('lat'),
'long'=>$session->get('long'),
'newregister'=>$session->get('newregister')
);
}
// return the user id
public function id()
{
return $this->id;
}
// the __get magic method is called when trying to retrieve a value of a
// property that has not been defined.
public function __get($name)
{
if(array_key_exists($name, $this->data))
{
return $this->data[$name];
}
return null;
}
// the __set magic method is called when trying to store a value in a property
// that has not been defined.
public function __set($name, $value)
{
global $session;
// check if the key exists in the 'data' array.
// if so, set the value in the array as well as the session
if(array_key_exists($name, $this->data))
{
$this->data[$name] = $value;
$session->set($name, $value);
}
}
}
This way you can still get and set values the same as you were, but will also store the set the value in your session class.
To test this:
$user = new User;
if($user->id())
{
echo $user->disp_name;
$user->disp_name = 'new name';
echo $session->get('disp_name');
}
I would not suggest you that because:
It is not a good practice to select an architecture "in case of future need" ('the reason I made it was so I could easily change'). Check http://www.startuplessonslearned.com (Eric Ries) or http://highscalability.com articles
Your code is hard/impossible to test (See Misko Hevery's blog (A google evangelist) http://misko.hevery.com for further information).
You are using "global" (never a good idea if you want to keep track of the dependencies).
It is better to seperate "the business logic" (a User class) and the wiring/building (a factory class for example). (See http://en.wikipedia.org/wiki/Single_responsibility_principle and "separation of concerns")
For really good code examples (and to understand which OO laws should not be broken), I can advice you Misko's blog (Also do not miss his technical talks at google that you can find on youtube). I am sure you will love them.
Hope this helps.