I am trying to create a PHP application using the MVC pattern. I am new to MVC and I do not use a framework since I want to understand in more depth the underlying MVC processes.I have a question regarding models, JSON representation and foreign keys. Lets assume that I have two models:
Class User {
public $id;
public $name;
public $gender_id;
}
Class Gender{
public $id;
public $description
}
The User->gender_id is a foreign key to Genders->id table.
My URI is /users/1 which is supposed to return the following JSON:
{"id":1,"name":"john","gender":"male"}
Is the controller's duty to know the model relations and retrieve the necessary data (user & gender model) which will then be passed to a view that will output the JSON ? Or should the association be defined somehow in the User's model? The gender description is a descriptive attribute but somehow it must be included in the Users view.
Regards
It is not the controller's duty to retrieve persisted data, you should have a Mapper for that. There are many answers that explain in detail the functions of Models and Controllers (this is a very good one).
Class User
{
private $id;
private $name;
private $gender;
public function __construct($id, $name, Gender $gender)
{
$this->id = $id;
$this->name = $name;
$this->gender = $gender;
}
public function getId()
{
return $this->id;
}
... and other getter/setters
public function setGender(Gender $gender)
{
$this->gender = $gender;
}
public function getGender()
{
return $this->gender;
}
}
And your gender class thus:
Class Gender
{
private $id;
private $description;
public function ($id, $description)
{
$this->id = $id;
$this->description = $description;
}
//with getters and setters
}
You can proceed to make a 'Service' (for lack of better word) to return your a JSON string of your User object. Note that this 'Service' is considered to be a part of your Model (see this)
Class UserToJSON
{
public function convert(User $user)
{
$user_arr = array();
$user_arr['id'] = $user->id;
$user_arr['name'] = $user->name;
$user_arr['gender'] = $user->gender->getDescription();
return json_encode($user_arr, true);
}
}
With all this in place (assuming you have a valid User object), you can get your JSON for a user easily like so:
$user = new User(1, "name", new Gender(1, "female"));
print (new UserToJSON())->convert($user);
Related
I would like to make a simple ORM in PHP for standard CRUD interaction with my db, I also want make it work in php5 for legacy compatibility.
I've written some classes to do this and it works, but not completely as I would.
This is the idea. I have an abstrac class called ModelBase which has a property (tableName) and some metods like select, insert, update and delete, plus has an abstract method, getData, that will be implemented by the classes that will be implement ModelBase and should return object of correct type.
So, for example, I could have a class Users which implements ModelBase and one another class UserData which is the model with the property.
Here is the code:
abstract class ModelBase{
private $tableName;
public function __construct($tableName) {
$this->tableName = $tableName;
}
public function select{
// make select query to db and retreive data
// ...
$resData = [];
while($dataRow = mysqli_fetch_array($res, MYSQLI_ASSOC)) {
$resData[] = $this->getObjectData($dataRow); // implemented in child class
}
return $resData;
}
public function insert(){ /* ... */}
public function update(){ /* ... */}
public function delete(){ /* ... */}
abstract function getObjectData($data); // maps the results
}
class UserData {
public $id;
public $name;
public $surname;
public $email;
// other fields
public function __construct() {}
}
class User implements ModelBase {
private $tableName = 'users';
public function __construct() {
parent::__construct($this->tableName);
}
public function getObjectData($dataRow) {
$o = new UserData ();
// mapping dataRow to object fields
$o->id = $dataRow['ID'];
// ....
return $o;
}
}
So I use my classes in this way:
$users = new Users();
$u = users->select();
$firstUser = $u[0]; // I get my user if exists
In $firstUser I'll get my object with property and correct data but I would like to have that also my IDE (vsCode in this case) would recognize the object type in order to suggest the correct properties. So if I write $firstUser-> I would like to see field suggestions (id, name, surname, ...) from UserData and for other xyzData classes as well.
What I should do to improve my classes in order to see property suggestions when I use my objects, also in php5?
Solution for PHP 8, tested on PHPStorm.
<?php
class Base {
/**
* #return static[]
*/
public function select() : array {
return [new self];
}
public function selectFirst() : static {
return $this->select()[0];
}
}
class User extends Base {
public ?string $userName = null;
}
#detects the current class via () : static
(new User)->selectFirst()->userName;
#detects the current class via #return static[]
(new User)->select()[0]->userName;
In line solution for PHP 5, define the variable directly with this comment
/** #var $a User */
$a->userName;
There is no benefit of supporting old PHP 5. You lose so mutch clean code and modern approach when supporting old php versions.
But when you have to, then go with the inline solution.
Not tested and not so clean for PHP 5:
class User extends Base {
public ?string $userName = null;
/**
* #return User[]
*/
public function select() : array {
return parent::select();
}
}
I have a class User which has private properties of a user and public interfaces. I am trying to expose those properties through OOP in order to bind their $key=>value to some sql parameters, but I can iterate over private members in order to extract the data. I have saw the var_dump() method which is usually used for debugging, so I created a public interface userInfo() that would return that object as an array. However, that just seems like a hack and not a real eloquent way of exposing or sharing data across this application. Ive read tons of articles and in class we are taught to keep members private and expose them through public interfaces. I know there is the protected member access, but is that eloquent enough? So, how would a "good", and I know that subjective, coder share this data across the application?
EDIT: Cannot use protected because nothing extends the user class
class User{
private $firstname
private $lastname
private $username
private $password
public getFirstName(){}
public getLastName(){}
public getUsername(){}
public getPassword(){}
public userInfo(){
return get_object_vars($this)
}
}
PHP has overloading / magic methods exactly for this purpose. You don't need to programmatically think of every possible value / field. Have a look at the below example:
class MyClass {
private $firstField;
private $secondField;
public function __get($property) {
if ( property_exists($this, $property) ) {
return $this->$property;
}
}
public function __set($property, $value) {
if ( property_exists($this, $property) ) {
$this->$property = $value;
}
return $this;
}
}
I understand the traditional naming convention for getter methods:
getName();
getAddress();
getAge();
But i dont understand why. I would rather just use the property name as the method as in the following example:
class Person
{
private $name;
private $address;
private $age;
public function __construct($name, $address, $age)
{
$this->name = name;
$this->address = address;
$this->age = age;
}
public function name()
{
return $this->name;
}
public function address()
{
return $this->address;
}
public function age()
{
return $this->age;
}
}
This makes code more fluent to me
i.e. $person->address()
But i get pulled up on code reviews when doing this for not using proper naming conventions.
Is there anything fundamentally wrong with not using get/set prefixes ?
Generally it's good for methods to contain a verb. This way the method's name is more self explanatory.
More info and Source: http://pear.php.net/manual/en/standards.naming.php
I am fairly new to php phalcon and the language itself. I am making a website that involves an abstract class and derived classes.
The Abstract Class:
<?php
abstract class UsersAbstract extends \Phalcon\Mvc\Model {
private $Full_Name; //
private $Mobile_Number; //
private $Email_ID; //
private $City; //
private $Country; //
private $DoB; //
private $Gender; //
private $Age; //
private $Availability_Flag; /*value of flag is 1 for active and 0 for inactive*/
/********setters and getters for all data members which I have not written here*******/
public function getSource()
{
return 'users_abstract';
}
}
The derived class
<?php
class UserPatients extends UsersAbstract
{
private $Patient_ID;
private $Guardian_Name;
private $Doctors = array();
public function getSource()
{
return 'user_patients';
}
/***************setter and getter for patient id*******************/
public function getPatientID()
{
return $this->Patient_ID;
}
public function setPatientID($value)
{
$this->Patient_ID = $value;
}
/****************setter and getter for guardian name******************/
public function getGuardianName()
{
return $this->Guardian_Name;
}
public function setGuardianName($value)
{
$this->Guardian_Name = $value;
}
/****************getter and setter for doctor array*********************/
public function getDoctors()
{
return $this->Doctors;
}
public function setDoctors($value)
{
$this->Doctors = $value;
}}
In my controller, I am reading data from a form in a view. An object of the class UserPatients is declared and its data members are set. Here is the action method that I am calling in the controller PatientSignInController:
public function SaveAction()
{
if (!empty($_POST)){
$patient_data = new UserPatients();
$patient_data->setFull_Name($_POST['Full_Name']);
$patient_data->setGuardianName($_POST['Guardian_Name']);
$patient_data->setDoB($_POST['DoB']);
$patient_data->setAge($_POST['Age']);
$patient_data->setGender($_POST['gender']);
$patient_data->setMobile_Number($_POST['Contact_No']);
$patient_data->setEmail_Address($_POST['Email_ID']);
$patient_data->setCity($_POST['City']);
$patient_data->setCountry($_POST['Country']);
$patient_data->setPatientID($_POST['Patients_ID']);
$patient_data->setFlag(1);
$doctorsArray = array("ACD","ABC"); //some hard coded values for the time being
$patient_data->setDoctors($doctorsArray);
$patient_medical_info = new PatientInfo();
$patient_medical_info->setBP($_POST['BP']);
$patient_medical_info->setTemperature($_POST['Temperature']);
$patient_medical_info->setInformation($_POST['Info']);
$patient_medical_info->setPatientID($_POST['Patients_ID']);
$patient_medical_info->setDateOfMeeting(date("d/m/y"));
$patient_data->save();
$patient_medical_info->save();
$this->response->redirect("../../../../WebMCare/PatientSignIn/Index");
}
}
The structure of my database is the same as these classes - I have a table Users_Abstract with all the attributes of the abstract class and I have a table User_Patients with all the attributes of the child class.
When I hit the save button on the form in the view and check the database tables, I discover that the new entries are added in the patient_info table. Which means that the $patient_medical_info->save() is working.
However, the table users_abstract does not contain any new data for the corresponding object, while the table user_patients does get the associated data. I checked messages for errors in save method, but it returned nothing.
I can't seem to find anything online.
Please help me.
I have tried storing data in a single table by associating one table, with all 12 attributes, to the classes UsersAbstract and User_Patients. Even that does not work.
Here is the data Iterator implementation
//Data Iterator
class DataIterator implements Iterator
{
public $data ;
public function __construct(Data $obj)
{
$this->data = $obj;
}
public function rewind()
{
$this->properties = get_object_vars($this->data);
}
public function valid()
{
if (key($this->properties) === null )
{
return false;
}
return true;
}
public function key()
{
return key($this->properties);
}
public function current()
{
return current($this->properties);
}
public function next()
{
next($this->properties);
}
}
and here is data class
/*Data Class*/
class Data implements IteratorAggregate
{
public $name;
private $age;
protected $address;
public $country;
public $state;
public function __construct($name, $age, $address, $country = 'USA', $state = 'NH')
{
$this->name = $name;
$this->age = $age;
$this->address = $address;
$this->country = $country;
$this->state = $state;
}
function getIterator()
{
return new DataIterator($this);
}
}
And here is the calling part
$data = new Data('Joker', '27', 'California');
foreach($data->getIterator() as $key => $value)
{
echo $key , ' ', $value, '<br>';
}
output
name Joker
country USA
state NH
Notice that the output does not contain my private and protected properties (age, address) output.
How do I tell Iterator to output those as well?
You cannot tell the iterator to output those properties because they are simply not accessible from the outside (i.e. the point where the iterator does get_object_vars($this->data).
There are two ways you could go about doing this:
By having the data object pass the values to the iterator.
Use the reflection API to pull them out by force (verbose, slow!).
But before going ahead with #1 as the preferred option, stop for a moment and ask yourself: why does the iterator expose non-public members of the data object?
Making something private means "You people don't really need to know about this; it may go away in the future, or it may change beyond recognition". If it's something that the outside world cares about, then why is it not public (either directly, or exposed through a public getter)? A rethink of what this iterator's purpose is might be in order.
That said, here's how you would do #1:
class DataIterator implements Iterator
{
public $data;
private $properties;
public function __construct(Data $obj, array $propeties)
{
$this->data = $obj;
$this->properties = $properties;
}
public function rewind()
{
// Arguably horrible trick to refresh the property map without
// demanding that Data exposes a separate API just for this purpose
$newIterator = $this->data->getIterator();
$this->properties = $newIterator->properties;
}
}
class Data implements IteratorAggregate
{
function getIterator()
{
return new DataIterator($this, get_object_vars($this));
}
}
Public, private and protected are access modifiers. They are designed to restrict the accessibility of your class attributes.
Public means that any one can access that attribute, so if someone wants, they can change the value, without that you know it.
Private mean that the attribute is only accessible INSIDE the class,
so nobody can "mess" with those properties from OUTSIDE the class.
Protected is similar like Private, but child classes (classes that
inherit from that class) have access to it.
You are making age and address private, so you are basically saying, nobody is allowed to access these attributes. If you want to access private/protected attributes, you will have to make getters and setters and call these functions, or make the attributes public.
try get_class_vars
$this->properties = get_class_vars(get_class($this->data));
instead of
$this->properties = get_object_vars($this->data);