I have a question about Dependency Injection in PHP.
I currently have this 3 classes:
Staff.php
<?php
class Staff
{
public function name($id)
{
return 'returning staff with id ' . $id;
}
}
Projects.php
<?php
class Projects
{
..... projects related functions
}
ProjectsManager.php
<?php
class ProjectsManager
{
private $staff = null;
private $projects = null;
public function __construct(Staff $staff, Projects $projects)
{
$this->staff = $staff;
$this->projects = $projects;
}
public function staff()
{
return $this->staff;
}
public function projects()
{
return $this->projects;
}
}
Those classes are instantiated like this:
$staff = new Staff;
$projects = new Projects;
$app = new ProjectsManager($staff, $projects);
echo $app->staff()->name(5);
The above is working, but what I would like to do is something like this:
$employee = $app->staff(5);
echo $employee->name();
echo $employee->position();
echo $employee->email();
How can I handle the dependency to achieve this?
You can simply add the set function in Staff class and call it in ProjectsManager:
<?php
class Staff
{
private $id = null;
public function name()
{
return 'returning staff with id ' . $this->id;
}
public function setId($id)
{
$this->id = $id;
}
}
class Projects
{
//..... projects related functions
}
class ProjectsManager
{
private $staff = null;
private $projects = null;
public function __construct(Staff $staff, Projects $projects)
{
$this->staff = $staff;
$this->projects = $projects;
}
public function staff($id = null)
{
$this->staff->setId($id);
return $this->staff;
}
public function projects($val = null)
{
return $this->projects;
}
}
$staff = new Staff;
$projects = new Projects;
$app = new ProjectsManager($staff, $projects);
$employee = $app->staff(5);
echo $employee->name();
$employee = $app->staff()->name(5);
//$app is the ProjectsManager
//$app->staff() returns it's Staff object
//staff()->name(5) Invokes the Staff object's name function
//Returns 'Returning staff with id 5'
echo $employee->name();
echo $employee->position();
echo $employee->email();
To avoid confusion, I would also suggest prefix some of those functions with get (eg. $app->getStaff()->getFromId(#))
Also, be sure to modify staff()->name(#) to actually return an object and not a string.
Related
Haven't found an answer yet but I'm sure there must be one: how do I prevent an object recursion/loop when objects reference each other? An example:
class Patient {
private $Issues = array();
[...]
public function __construct($id) {
[ Get data from DB ]
while ($row = $result->fetch_assoc()) {
$this->Issues[$row['idIssue']] = new Issue($row['idIssue']);
}
[...]
}
}
class Issue {
private $Patient;
[...]
public function __construct($id) {
[ Get data from DB ]
$this->Patient = new Patient($row['idPatient']); <-- Leads to recursion as the patient will load all it's Issues() etc. etc.
[...]
}
}
How do I prevent this? I could use the id of the Patient() instead of the real object but that feels like a hack. Is there a way to use the real object?
Do not recreate object. Just pass the instance of the master object to the detail constructor. E.g.:
class Patient {
private $Issues = array();
[...]
public function __construct($id) {
[ Get data from DB ]
while ($row = $result->fetch_assoc()) {
$this->Issues[$row['idIssue']] = new Issue($row['idIssue'], $this);
}
[...]
}
}
class Issue {
private $Patient;
[...]
public function __construct($id, Patient $patient) {
[ Get data from DB ]
$this->Patient = $patient
[...]
}
}
You can (should !) separate the DB connection/queries from the entities definitions and pass references to relations, otherwise, you can't mock entities, plus mixing DB connection and entities definition goes against the separation of concerns :
// somewhere in your code
$idPatient = 42;
$patient = new Patient();
$patient->setId($idPatient);
// get datas from DB
while ($row = $result->fetch_assoc())
{
$issue = new Issue();
$issue->setId($row['idIssue'])
->setPatient($patient);
$patient->addIssue($issue);
// or, shorter way :
// $patient->addIssues((new Issue())->setId($row['idIssue'])
// ->setPatient($patient));
}
class Patient {
private $Issues = array();
private $Id;
public function addIssue(Issue $issue): self
{
$this->Issues[] = $issue;
return $this;
}
public function setId(int $id): self
{
$this->Id = $id;
return $this;
}
}
class Issue {
private $Patient;
private $Id;
public function addPatient(Patient $patient): self
{
$this->Patient = $patient;
return $this;
}
public function setId(int $id): self
{
$this->Id = $id;
return $this;
}
}
I want to be able to use an object like below, to retrieve new orders and new invoices. I feel like it is most readable, but I am having trouble writing the PHP class to work this way.
$amazon = new Amazon();
$amazon->orders('New')->get();
$amazon->invoices('New')->get();
In my PHP class, how would my get() method be able to distinguish whether to return orders or invoices?
<?php
namespace App\Vendors;
class Amazon
{
private $api_key;
public $orders;
public $invoices;
public function __construct()
{
$this->api_key = config('api.key.amazon');
}
public function orders($status = null)
{
$this->orders = 'orders123';
return $this;
}
public function invoices($status = null)
{
$this->invoices = 'invoices123';
return $this;
}
public function get()
{
// what is the best way to return order or invoice property
// when method is chained?
}
}
A couple of ways, if you want it dynamic and don't do any logic in the methods, use something like __call
<?php
class Amazon {
public $type;
public $method;
public function get()
{
// do logic
// ...
return 'Fetching: '.$this->method.' ['.$this->type.']';
}
public function __call($method, $type)
{
$this->method = $method;
$this->type = $type[0];
return $this;
}
}
$amazon = new Amazon();
echo $amazon->orders('New')->get();
echo $amazon->invoices('New')->get();
If you want to do logic in the methods, do something like:
<?php
class Amazon {
public $type;
public $method;
public function get()
{
return 'Fetching: '.$this->method.' ['.$this->type.']';
}
public function orders($type)
{
$this->method = 'orders';
$this->type = $type;
// do logic
// ...
return $this;
}
public function invoices($type)
{
$this->method = 'invoices';
$this->type = $type;
// do logic
// ...
return $this;
}
}
$amazon = new Amazon();
echo $amazon->orders('New')->get();
echo $amazon->invoices('New')->get();
As orders and invoices are set methods, I would suggest to do as follows:
public function get(array $elements)
{
$result = [];
foreach($elements as $element) {
$result[$element] = $this->$element;
}
return $result;
}
So, you can call get method as:
$amazon = new Amazon();
$amazon->orders('New')->invoices('New')->get(['orders', 'invoices']);
** You need to validate the element's availability within the get method.
Not: Its work just one time in loop. Its return this error for other time.
I have a usermodel.php in models. When i use it like
$this->load->model("Usermodel");
$user = $this->Usermodel->quer(1);
it throw "Message: Undefined property: CI_Loader::$Usermodel"
When i use
$this->load->model("Usermodel");
$user = new Usermodel();
it throw "Message: Cannot redeclare class Users"
user class has construct and desturct functions. I call it in Usermodel.php file. And usermodel has construct and destruct functions.
<?php
class User {
public function __construct(){
parent::__construct();
}
private $id;
private $email;
private $name;
private $profilPic;
private $topPic;
private $gender;
private $birthday;
private function setid($id){
$this->id = $id;
}
private function getid(){
return $this->id;
}
private function setemail($email){
$this->email = $email;
}
private function getemail(){
return $this->email;
}
private function setname($name){
$this->name = $name;
}
private function getname(){
return $this->name;
}
private function setprofilPic($profilPic){
$this->profilPic = $profilPic;
}
private function getprofilPic(){
return $this->profilPic;
}
private function settopPic($topPic){
$this->topPic = $topPic;
}
private function gettopPic(){
return $this->topPic;
}
private function setgender($gender){
$this->gender = $gender;
}
private function getgender(){
return $this->gender;
}
private function setbirthday($birthday){
$this->birthday= $birthday;
}
private function getbirhday(){
return $this->birthday;
}
public function __set($name, $value){
$functionname = 'set'.$name;
return $this->$functionname($value);
}
public function __get($name){
$functionname = 'get'.$name;
return $this->$functionname();
}
public function __destruct(){}
}
?>
This is usermodel
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Usermodel extends CI_Model {
public function __construct(){
parent::__construct();
$this->load->view("Users.php");
$this->load->model("Dbmodel");
}
public function quer($id){
$uqcont = array("id" => $id);
$uiqcont = array("userID", $id);
$uq = $this->Dbmodel->control("user", $uqcont);
$uiq = $this->Dbmodel->control("userinfo", $uiqcont, $limit=1, 'userID');
$user = new Users();
if($uq->num_rows()==1){
$uq = $uq->result();
$user->id=$id;
$user->name=$uq[0]->name;
$user->email=$uq[0]->email;
$user->profilPic="girlprofil.png";
$user->topPic="arka.jpg";
}
if($uiq->num_rows()==1){
$uiq=$uiq->result();
if($uiq[0]->profilPic){
$user->profilPic = $uiq[0]->profilPic;
}
if($uiq[0]->topPic){
$user->topPic = $uiq[0]->topPic;
}
}
return $user;
}
public function __destruct(){}
}
?>
This is a part of my view.php
foreach($query->result() as $row){
$cont = array("id" => $row->userID);
$query = $this->Dbmodel->control("user", $cont);
$this->load->model("Usermodel");
$user = new Usermodel();
$user = $user->quer($row->userID);
$date = new datetime($row->date);
$date = $date->format("d.m.Y H:i:s");
//$query = $query->result();
//foreach($query as $qur){
echo '$user->name.'<br>'.$row->comment;
//}
//unset($user);
}
Please look to my error and help me to solve it.
the class User is being declared more than once, probably in the loop you were referring to.
is this line in the loop?
$this->load->model("Usermodel");
if so try moving it out of the loop.
The error is due to loading the model several times in the foreach loop. Load it only once then create instances of the class as many times as you wish
$this->load->model("usermodel");
foreach($query->result() as $row){
$cont = array("id" => $row->userID);
$query = $this->Dbmodel->control("user", $cont);
$user = new Usermodel();
$user = $user->quer($row->userID);
$date = new datetime($row->date);
$date = $date->format("d.m.Y H:i:s");
}
Then consider using small caps in your load->model().
I advise loading the data in the controller then passing the data to the view. Let the controller have most of the logic.For example in the controller
$this->load->model('usermodel');
$data['users'] = $this->usermodel->quer($id)->result();
$this->load->view('users_view', $data);
In the view its as simple as
foreach ($users as $user)
{
//logic e.g. echo $user->name;
}
$this->load->model("X") is doing something like following;
Check models directory if X.php exists and if it exists
it creates the class with the given name in our case "X", [ $this->X = new X(); ]
you can also pass the alternative name to the load->model method like
$this->load->model("X","my_x_model"), in that case the loader module will create
$this->my_x_model = new X();
It was just to give some information about "what happens when you trying to load a model"
You're getting an Undefined property because
$this->load->model("usermodel");
has to be in lowercase.
https://www.codeigniter.com/userguide3/general/models.html#loading-a-model
I change this "class Users" to "class users extends CI_Model" and i move "$this->load->model("usermodel") on over of loop. Then the problem is solved. Thank you for help.
I wrote a class
class User {
private $cars = array(); //store class Car 's object
public function getCars()
{
return $this->cars;
}
public function setCars($cars)
{
$this->cars = $cars;
}
}
class Car{
private $model;
public function getModel()
{
return $this->model;
}
public function setModel($model)
{
$this->model = $model;
}
}
$user = new User();
$cars = $user->getCars();
$cars[0]->getModel();
When I try to access getModel() php report "Call to undefined method stdClass::getModel()" .
Is there the best practice to deal with such case?
Edit:I filled the getter and setter. In fact, It's generated by phpstorm.
Edit:I tried again and it works well with the demo code below. The original code is too complicated to show. Maybe I caused by my misunderstanding of copying by value and by reference of array.
Please ignore this question. sorry.
class User {
private $cars = array(); //store class Car 's object
public function getCars()
{
return $this->cars;
}
public function setCars($cars)
{
$this->cars = $cars;
}
}
class Car{
private $model;
public function getModel()
{
return $this->model;
}
public function setModel($model)
{
$this->model = $model;
}
}
$user = new User();
$car = new Car();
$car->setModel("Ford");
$arr = $user->getCars();
array_push($arr,$car);
$user->setCars($arr);
foreach($user->getCars() as $car) {
var_dump($car->getModel());
}
You haven't shown your [Getter Setter ] code. You need to create one with something like:
public function setCars($val){
$this->cars = $val;
}
public function getCars(){
return $this->cars;
}
The same applies for getModel()
I'm having problems with accessing variables from my classes...
class getuser {
public function __construct($id) {
$userquery = "SELECT * FROM users WHERE id = ".$id."";
$userresult = mysql_query($userquery);
$this->user = array();
$idx = 0;
while($user = mysql_fetch_object($userresult)){
$this->user[$idx] = $user;
++$idx;
}
}
}
I'm setting this class in a global 'classes' file, and later on I pass through a user id into the following script:
$u = new getuser($userid);
foreach($u->user as $user){
echo $user->username;
}
I'm hoping that this will give me the name of the user but it's not, where am I going wrong?!
Thanks
please define your users member as public in your class like this
class getuser {
public $user = null;
//...
}
in order to access a class property, you have to declare it public or implement getters and setters (second solution is preferable)
class A {
public $foo;
//class methods
}
$a = new A();
$a->foo = 'whatever';
with getters and setters, one per property
class B {
private $foo2;
public function getFoo2() {
return $this->foo2;
}
public function setFoo2($value) {
$this->foo2 = $value;
}
}
$b = new B();
$b->setFoo2('whatever');
echo $b->getFoo2();
in your example:
class getuser {
private $user;
public function __construct($id) {
$userquery = "SELECT * FROM users WHERE id = ".$id."";
$userresult = mysql_query($userquery);
$this->user = array();
$idx = 0;
while($user = mysql_fetch_object($userresult)){
$this->user[$idx] = $user;
++$idx;
}
}
/* returns the property value */
public function getUser() {
return $this->user;
}
/* sets the property value */
public function setUser($value) {
$this->user = $value;
}
}
$u = new getuser($userid);
$users_list = $u->getUser();
foreach($users_list as $user) {
echo $user->username;
}