PHP ORM how to set id in constructor - php

I have a basic problem with following code:
<?php
interface UserInterface
{
public function getId();
public function getName();
}
class User implements UserInterface
{
private $_id;
private $_name;
public function __construct($id, $name)
{
$this->_id = $id;
$this->_name = $name;
}
public function getId()
{
return $this->_id;
}
public function getName()
{
return $this->_name;
}
}
class UserMapper
{
public function insert(UserInterface $user)
{
... insertion code
}
}
?>
The insert method of the UserMapper expects an UserInterface object. So I create one:
<?php
$user = new User(1, "Chris");
$userMapper = new UserMapper();
$userMapper->insert($user);
?>
My problem is, that the user's id is an auto-increment value that is coming from the database after inserting the object. But the object's constructor forces me to define an id because the object would not be complete without an id. How to solve that general problem?
To pass the id as a second parameter to the constructor woth a default value is not an option, because in my understanding the object would be incomplete without having an id.
Thanks for your help.

You can pass null:
$user = new User(null, "Chris");
$_id is not checked for a valid integer and with null you know that this model has no valid ID yet.

Related

How to test generated id in aggregate root

I wanna to test my entities created from doctrine, but i don`t know how to test id if they are generated themself while object save to database.
class Dish
{
private ?int $id;
private Collection $prices;
public function __construct()
{
$this->prices = new ArrayCollection();
}
public function identifier(): string
{
return $this->id;
}
public function isEquals(Dish $dish): bool
{
return $this->identifier() === $dish->identifier();
}
public function addPrice(float $price)
{
$this->prices->add(new DishPrice($this, $price));
}
}
class DishPrice
{
use DeletedEntityTrait;
private ?int $id;
private float $price;
private Dish $dish;
public function __construct(Dish $dish, float $price)
{
$this->dish = $dish;
$this->price = $price;
}
public function identifier(): string
{
return $this->id;
}
public function isEquals(Dish $dish): bool
{
return $this->identifier() === $dish->identifier();
}
}
How to test isEquals methods in both classes?
Basically you have two options.
First option is functional test. You can create two entities and persist them to the database.
Second option is pass ID to the constructor. To be able to do this you can add method to repository that will provide you next ID for your entity in service layer:
interface DishRepository
{
public function getNextIdentity(): int;
}
$id = $repository->getNextIdentity();
$dish = new Dish($id);
and in unit-tests you can pass to the constructor whatever ID that you want
$testId = 111;
$dish = new Dish($testId);
Try to use uuid instead of int and testing will be easer) also, you will have more benefits while using uuids.

How can I access to public properties/method of an object represented by $this->myObject in my class?

I'm trying to access getters of a custom object. I wanted to send the object to the constructor params (I'm currently sending to the build method (but
My class representing an object that I'd like to display its values :
class User{
private $id;
private $name;
private $email;
public function __construct(int $id, string $name, string $email) {
$this->id = $id;
$this->name = $name;
$this->email = $email;
}
public function getName(): string {
return $this->name;
}
... // the other getters
}
In my Page :
$myUser = new User(1, "toto", "toto#url.com");
$blockUser = new UserBlock($sqlConnection);
$blockUser->setUser($myUser);
$blockUser->build();
interface IBlock :
interface IBlock {
function __construct($sqlConnection);
function build();
}
UserBlock class:
class UserBlock implements IBlock {
private $myUser;
public function __construct($sqlConnection) {
...
}
public setUser($myUser) {
$this->myUser = $myUser;
}
function build() {
echo "Name = ".$this->myUser->getName(); // ERROR: Doesn't know the type of $this->myUser so can't access to getName method.
$tempVarUser = $this->myUser;
echo "Name = ".$tempVarUser->getName(); // ERROR: Doesn't know the type of $tempVarUser so can't access to getName method.
}
}
With my code structure, how can I access to the properties and methods of the object User represented by the variable $this->myUser in the UserBlock class ?
I have to remove the implements of the Interface and send the User object to the build method as parameter (which is not allowing by my interface here).
Hope this is clear. Thanks for your help.

How to pass an object to a view in Laravel?

I want to pass an object of Student Model to a view in Laravel. I tried using
return view('student/main')->with($student);
Where $student is a instance of Student Model (Code is below)
But it gave an error "Illegal offset type"
I know that data can be passed as an array to a view. But I really want to pass it as a object if possible. Then I can display data as follows by fetching data from get methods.
<h4 class="text-left"><strong>{{$student->getName()}}</strong> </h4>
I am looking for a solution which can be done using keeping objects instead of arrays.(if possible)
The Student model code is as follows. It consists with simply setters and getters.
class Student extends Model{
//attributes
private $student_id;
private $first_name;
private $last_name;
private $batch_id;
// set attributes
public function setID($student_id)
{
$this->student_id = $student_id;
}
public function setFirstName($first_name)
{
$this->first_name = $first_name;
}
public function setLastName($last_name)
{
$this->last_name = $last_name;
}
public function setBatchID($batch_id)
{
$this->batch_id = $batch_id;
}
// get attributes
public function getName()
{
return $this->first_name." ".$this->last_name;
}
public function getID()
{
return $this->student_id;
}
public function getBatchID()
{
return $this->batch_id;
}
You've got a number of options to do that
return view('student/main', ['student'=> $student]);
return view('student/main', compact('student'));
return view('student/main')->with('student', $student);
return view('student/main')->withStudent($student);
You have to name your variable:
return view('student/main')->with(['student' => $student]);

Custom userIdentity class in yii2

I want to create custom userIdentity class according to my specific requirements .Here the code is
<?php
namespace app\models;
use yii\web\IdentityInterface;
use app\models\dbTables\Users;
class UserIdentity implements IdentityInterface{
const ERROR_USERNAME_INVALID=3;
const ERROR_PASSWORD_INVALID=4;
const ERROR_NONE=0;
public $errorCode;
private $_id;
private $_email;
private $_role;
private $_name;
public function findIdentityById($id){
$objUserMdl = new Users;
$user = $objUserMdl::findOne($id);
$userRole = $objUserMdl->getUserRole($user->user_id);
$this->_id = $user->user_id;
$this->_email = $user->email_address;
$this->_role = $userRole;
$this->_name = $user->full_name;
return $this;
}
public function getId()
{
return $this->_id;
}
public function getName(){
return $this->_name;
}
public function getEmail(){
return $this->_email;
}
public function getRole(){
return $this->_role;
}
public static function findIdentity($id)
{
return self::findIdentityById($id);
}
public function getAuthKey()
{
throw new NotSupportedException('"getAuthKey" is not implemented.');
}
public function validateAuthKey($authKey)
{
throw new NotSupportedException('"validateAuthKey" is not implemented.');
}
public static function findIdentityByAccessToken($token, $type = null)
{
throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
}
}
?>
Basically I have two tables roles and users and I want to set the specific properties from both table in yii::$app->user->identity
When I call the above code the findIdentity($id) function returns error for obvious reasons stating that I cannt call $this in static funtion . How can I set the required properties in function and return the instance of userIdentity class from it ?
I recommend reading this: When to use self over $this? you are really confusing the 2.
$objUserMdl = new Users;
$user = $objUserMdl::findOne($id);
$userRole = $objUserMdl->getUserRole($user->user_id);
You are calling :: on an object, you cannot do that.
I say delete what you have done and start again, it should be much easier then what you wrote. It would take a long time to show you how to do it properly, just look in the yii2 advance template and see how they are doing it. You can use your own identity class and set up any special attributes there. Just study the yii2 code.

PHP can't echo user's name from object

I'm trying to use a session var and call the logged in users details through object on the page. The user has already logged in through my login object. Here are my objects:
class User {
public $userdata = array();
//instantiate The User Class
public function User(){ }
public function set($var, $value) {
$this->userdata[$var] = $value;
}
public function get($var) {
if(isset($this->userdata[$var]))
{
return $this->userdata[$var];
}
return NULL;
}
function __destruct() {
if($this->userdata){
$this->userdata;
}
}
}
class UserService {
private $db;
private $fields;
public $user;
private $session;
public function __construct() {
$this->db = new Database();
}
// //get current user by ID
public function getCurrentUser($session) {
$this->session = $session;
$query = sprintf("SELECT * FROM User WHERE idUser=%s",
$this->db->GetSQLValueString($this->session, "int"));
$result = $this->db->query($query);
if($result && $this->db->num_rows($result) > 0){
//create new user
$user = new User();
$row = $this->db->fetch_assoc($result);
//set as object
foreach($row as $key => $value) {
$user->set($key, $value);
break;
}
return $user;
//return $this->user;
}
return NULL;
}
}
On my page I've check my session var has a value which it does, so I call the object like so.
$um = new UserService();
$user = $um->getCurrentUser($_SESSION['MM_Username']);
echo $user->get('UserSurname');
however, I see no user surname on the page. I have checked with a none object query and I see a surname but as soon as its object is doesn't work.
I think the problem is here:
foreach($row as $key => $value) {
$user->set($key, $value);
break; // you should probably remove it
}
You should use unnecessary break and probably after setting for example id you stop setting another object properties (UserSurname, Name and so on).
In addition it's quite confusing that inside $_SESSION['MM_Username'] you store idUser and not UserName
Code review
Marcin Nabialek allready answered your question. That break in the foreach is. well. what is it doing there?
But, there are much more things broken in your code. So here is a code review:
Constrcutors
You obviously know what a constructor is. You use it in both classes. But, differently. why? You User class has a public function User() but your UserService has a public function __construct(). Pick one, and stick to it. And if you can choose, pick the correct one: __construct()
From the phpdoc:
As of PHP 5.3.3, methods with the same name as the last element of a namespaced class name will no longer be treated as constructor. This change doesn't affect non-namespaced classes.
So namespacing your User class will break your constructor. This may not be a problem now, but it smells. Simply use __construct(). It is the prefered and correct way to do it. We live today, and not in the past of php4- days :)
Code styling
Oh god, a lot of kittens died today!
Sometimes you have a bracket on a new line:
if (isset($this->userdata[$var]))
{
return $this->userdata[$var];
}
and sometimes you don't
if($this->userdata){
$this->userdata;
}
Again, pick something and stick to it. and if you want to save some kittens. stick to the standards: PSR-1 & PSR-2
Public atributes, jeuk
Your User class has a public var $attributes. So it is accessible from the outside world. But you also give us a get and set method. why?
A good rule is: public $var smells, protected $var should be used with caution and private $var is the good stuff.
What are your classes? and why don't you use __constructors?
If I look at the User class, I always look at the __constructor. The User class needs no variables. So I should be able to do something like this:
$me = new User();
$me->getName(); //who am I ?
This ofcourse doesn't work. A User without a name doesn't make sense. It always has one. So ask for it!
class User
{
public function __construct($name)
{
$this->name = $name;
}
}
$me = new User('jeroen');
$m->getName(); //I am jeroen :)
If you need something ask for it ;)
So:
public function __construct(Database $db)
Is the way to roll!
Don't make me read the database
Now, your get/set methods are tigthly coupled with your database. If you change the name of a column in the database. You can refractor your entire code to get('new_column_name'); . Sounds like fun!
Also, what does the method say me? Nothing, does it write easy? no
getName says what it does, it gets me the name.
get tels me i'm getting something. but what?
other questions rise: get('name') =?= get('Name')
It's ok for the User object to know what it has.
Summary
Ok, I outlined some things wrong in your code. Some concepts you should look into:
SOLID
PSR standards
Factory Pattern (this will help you with your UserService
Inversion Of Control
So, for the sake of the article, here is your code revamped. Note that I wrote it into this commentbox directly, so I could have missed some things and made some errors.
Changelist:
I cleaned up styling
I added some comments
removed _destruct() (it wasn't doing anything)
Used PDO instead of Databse class (no idea what you are using, but looks like a PDO wrapper)
changed some table names and the select query (never use * in a selct query. Onyl ask for that what you need)
used prepared statements
more flexibility
exceptions instead of returning null;
Your $DBH could be put into a singleton/factory to ease your $dbh creation: Database::getInstance(); or DatabaseFactory::getInstance()->createPDO(); or so. Long time since I wrote something like this
Usage:
$DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
$userRepository = new UserRepository($DBH);
$id = $_SESSION['MM_Username'];
try
{
$me = $userRepository->find($id);
}
catch( UserNotFoundException $e )
{
//user not found
}
print $me->getSurName();
User class:
class User
{
/**
* If the User persists in the DataBase
* $id holds it's db id
* #var int
*/
private $id;
/**
* #var String
*/
private $surName;
/**
* #var String
*/
private $firstName;
/**
* #param String $firstName
* #param String $surName
* #param int $dbId
*/
public function __construct($firstName, $surName, $dbId=null)
{
$this->id = $dbId;
$this->firstName = $surName;
$this->surName = $surName;
}
/**
* Does the user ecist in the DB?
* #return boolean
*/
public function hasId()
{
return $this->id !== null;
}
/**
* #return int
* #return null user doesn't persist in DB
*/
public function getId()
{
return $this->id;
}
/**
* Return the users full name
* The fullname consists of his FirstName and SurNAme
* #return String
*/
public function getName()
{
return $this->firstName . ' ' . $this->surName;
}
/**
* #return String
*/
public function getSurName()
{
return $this->surName;
}
/**
* #return String
*/
public function getFirstName()
{
return $this->firstName;
}
/**
* Setters: we return $this to allow chaning
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
// ... other methods here. You can add extra swet stuff.
// for instance check or it is a valid firstName, or email or ...
//I removed your __destrouct, because wtf? it isn't doing anything at all
}
and your UserRepository:
/**
* The UserRepository queries the database and get's you your users
*/
class UserRepository
{
private $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
public function find($id)
{
$statement = $this->db->prepare('SELECT id,first_name,sur_name FROM users WHERE id = :id');
$statement->execute(array(
'id' => $id
));
if ( null === ($user = $statement->fetch(PDO::FETCH_ASSOC)) )
{
throw new UserNotFoundException();
}
return new User(
$user['first_name'],
$user['sur_name'],
$user['id']
);
}
}
and the exception:
class UserNotFoundException extends Exception();

Categories