Right way? PHP Object for Database Entity - php

I'm struggling with this kind of issue and I can't find direct answer to my question through Google.
Let's say we have a table 'users' in the database and it has the following columns: id, username, password, real_name.
Then my problems starts after that.
I know we can make an independent class for that like:
class User
{
private $_id;
private $_username;
private $_password;
private $_real_name;
// getters
// setters
}
Now, should I add functions like fetchById on that class? I mean, is it a good practice?
public function fetchById($id)
{
// validate param
// query database
// copy results to appropriate properties
}
Or should it be done by another class, like UserManager? Then for every result, we convert the array result to of that object?
Also, where should I put functions like fetchUsers where it will fetch multiple users from the database? Functions which deals with multiple records of the same entity.
I am looking for code efficiency and performance.
I know 'some' ideas but I can't justify it. I need help.
I'm currently working with CodeIgniter but I think this problem is PHP OOP in general.

For me personally, I have my models (objects that represent database tables) extend an abstractModel object that has the ID attirbute and shared static functions like fetchById(). This abstract model also has methods like save() which use the ID of the object to save.
You don't have to have an 'id' field in the table, the id of the model just has to be one of the unique key fields in the table.
Instead of fetchUsers() I have a generic loadAll() static function in the abstract class. Thus you could call Users::loadAll() to get all the models of your users. This means that most of your models can be interfaced with in the same way and reduces duplication of code. Of course if there are methods specific to the model, then you will need to define them in child model.

Build a class for table management and another for entity.
see other ORMs like doctrine, propel, or frameworks ORM like cakephp.

Related

Loading multiple rows from a model? (PHP, MySql)

One of the things that I don't fully understand about the MVC model is loading multiple data tables from the model.
Let's say we have a Model called user.
public class User{
protected $username;
protected $email;
protected $id;
public function setUsername($name, $id) {
$db->set->name($name)
$db->set->id($id)
}
public function getUsernameById($id) {
return $db->get->name($id);
}
As far as I've learned, the model is equivalent to a single row in the database (called user).
Now I have written two functions. The first is for setting the username and the other is for retrieving the username through an ID.
Suppose you want to retrieve all users. Let's say through the getAllUsers() method. This method does not fit on the Model as it is not a single object.
Now I understand that for example, you can call this method (function) in your controller itself. But where can you define this method (function)? Since you don't do this on the model.
I also like to hear it if I am wrong :)
Basically your definition is correct:
Model objects hold data and define the logic for manipulating that
data. For example, a Student object in the Basic sample application is
a model object. It holds data describing facts about the object like
the first and last name of the student and has methods that can access
and change this data (getters & setters). Model objects are not
directly displayed. They often are reusable, distributed, persistent
and portable to a variety of platforms.
(from: http://best-practice-software-engineering.ifs.tuwien.ac.at/patterns/mvc.html)
So where to put the getAllUsers() function in case you do not just put it into the model?
Well first of all when we are talking about the term "model" we do not always mean model. In MVC the model (M) contains all of the business logic. This means it contains the domain model which is the model you are meaning in this question. But it also contains the service layer.
The service layer usually consists of various classes which provide functionalities like for instance getAllUsers() or getAllUsersWhichOwnAGreenTruck().
Here is a great graphic demonstrating this:
(Source: Domain Model and Service Layer patterns in P of EAA)
In modern applications e.g. built with the Laravel PHP Framework you usually generate the database as well as the model and the database mapping/communication automatically (Example Eloquent ORM: https://laravel.com/docs/5.8/eloquent). But you start implementing complex application logic within the service layer.

How can I render Master Detail data in MVC using PHP?

I can't find an answer to this anywhere. I've seen code examples in C# with ASP.NET MVC, but nothing in PHP outside of a CMS or framework.
If I have two tables, students and classes, how do I get that data rendered in the view?
If I query two different tables in one model (same function/method, however), does that somehow violate the principles of MVC? What about 20 different tables? That sounds like a lot of overhead for nothing a one off table you might only query once or twice. Do I really need 20 models?
Can someone please show me this in straight PHP with no frameworks and no CMS?
EDIT: This is for building my own MVC, so "straight" PHP means me building this component.
My approach would probably be to use a repository for classes and users in this case.
StudentRepository
Retrieves instances of students class based on some criteria. This might have a method called getStudentsByClass($classID) that would retrieve the students for a class by it's class id.
SchoolClassRepository
Retrieves instances of a school class based on some criteria. For instance, classes for a semester.
getClassesBySemester($semesterID)
Then in your regular "SchoolClass" class, I would have a function that uses the StudentRepository to retrieve classes for the current class. For instance:
<?php
class SchoolClass(){
private $id;
private $students;
public function getStudents(){
$repo = new StudentRepository();
return $repo->getStudentsByClass($this->id);
}
}
?>
This would mean that your queries to find students for a class would not be exposed in your SchoolClass model. The only thing that the SchoolClass knows about is that the StudentRepository returns the users that it needs.
You would then pass a SchoolClass model as your data model to grab information in your view.

Where to put not Eloquent related SQL queries

I use Eloquent to implement my models. It has a lot of methods that makes working with models much easier. However, in some cases I have to implement functionality that is accessing the database, but doesn't return an Eloquent model.
For example, imagine a common User model. Eloquent helps me a lot in CRUD operations for the User model, but what if I have to implement a method that returns some statistics about users (ex. how many total users, how many active users, etc.).
Where should I implement this functionality? In the Eloquent model, as a public static function, (e.g. User::getStats()), or should have a different class for this.
It seems a little bit unnatural to have these methods on a Eloquent model, because they are not Eloquent related.
It's really up to you, but it's a good idea to decide on a convention and stick to it.
My rule of thumb is this:
if it's related to a single item that is normally represented by an Eloquent model, do it as a public static method on that model. For example, in my current project, I have a Contract model. I needed a method that would generate a contract ID string (for compatibility with legacy systems) for new contracts. This is related to a single item (a contract) but for technical reasons needed to be generated separately from the Eloquent model (ie. it was based on a separate database with a different connection). So I created a public static method: public static function generateContractIdentifier($id, $salesRep). If you're using a repository for access to your database, you could put it there.
If it's more general (ie. not tied to an instance of the Eloquent model), I put it into a separate library. For example, in the same application I have a queue for processing items for printing (an industrial process). The application has a dashboard and I needed to show the current queue status for management. For this I created a ProcessStatus library and implemented a method: public function getStatus() which runs a query and provides the results as an array for display in a view. This is not related to any particular item in the queue, so I put it in a separate library.
As always, it depends on your project and what role the user plays in it.
But basically, no, I don't think the logic for building reports belongs on the user model. While it may be related to the user, regarding the SOLID-principle the User class should only have one responsibility, which in this case is to handle the User entity.
This contains getting and setting properties on an instance, in a simple project it's probably also fine to define some scopes on the model, e.g. to select only active users, like User::getActive();
But as your project grows, you should consider using more specific classes.
For instance, you could abstract the Eloquent functionality into a User-Repository. So now you have a handler for operations on the entitiy itself, like
$userRepo->getAll();
$userRepo->getActive();
$userRepo->getInactive();
and a handler for a User instance:
$user->getName();
$user->setStatus();
Creating reports and statistics is yet a completely different topic. So you could have something like a UserReportBuilder oder UserStatisticsService:
$userStats->getMostActive();
$userStats->getRegistrationsPerDay();
A simple example:
// UserRepository:
class UserRepository
{
protected $model = $model;
public function __construct($model)
{
// you pass in an instance of the actual Eloquent model
// so you have the whole power of eloquent in here
$this->model = $model;
}
public function getActive()
{
// this returns a collection of models
return $this->model->where('status', 'active')->get();
}
}
$userRepo = new UserRepo(new User);
And that's pretty much it. You can still work with Eloquent, but you have separated the functionality in parts with a clewar responsibility. So your UserStats class would only be resposible for building user statistics:
class UserStats
{
// You could pass in the Repository through the constructor
// or just use the Eloquent model directly
public function getRegistrationsPerDay()
{
return User::groupBy('day')->get(
[
DB::raw('DATE(created_at) as day'),
DB::raw('count(*) as registration_count')
]
);
}
}
The User instance or the UserStats-builder do not need to know how to fetch all users, and the User instance or the UserRepository do not need to know how to calculate registrations per day, so it makes sense to split that functionality into separate, independent parts that do exactly one thing.
I think you get the idea and I hope it makes sense. Maybe you should make yourself more familiar with the SOLID-principles and try to keep them in mind when you get stuck on problems like that.

Populating Objects from Data base POO

I have a question regarding how to populate objects from the Data base, it's more about the architecture than populating it self.
Let's say I have a table called receipts which has: receipt_id, user_id, issue_date, business_id, product_id abd type.
Now I want to create a Receipt class which will be populated from that table, I would do something like this:
class Receipt {
public function __construct($receipt_id = null) {
if(!is_null($receipt_id))
$this->populate($receipt_id);
}
public function populate($receipt_id){
//Get data from data base
}
public function save(){
//Saves the current receipt into the data base.
}
public static function getReceiptsFromUser($user_id){
}
}
My question is about getReceiptsFromUser, should it be static?. It makes sense for the User class to have a method called getReceipts which would return an array of receipts objects calling this static method. I think it should be static because it doesn't make any sense to create an empty receipt to generate the user's receipts. Is there any flaws in this architecture or better aproaches?
Yes, it should be static. As you already mentioned yourself, you don't need a Receipt object in order to retrieve other Receipts.
From the PHP documentation:
Declaring class properties or methods as static makes them accessible without needing an instantiation of the class.
By the way, what you're doing here is called the Active Record pattern. If you're going to use this approach, it's a good idea to create a base class (you can call it Record or something similar) that defines the shared methods (such as save, find, etc.) and let your models extend them (class Receipt extends Record, class User extends Record, etc.). This way, you don't need to repeat this code in all of your models.
A nice (PHP) example for this approach is given by the PHP ActiveRecord project. A look into their documentation should give you some ideas about how it works.
Another approach is the Data Mapper pattern. The benefit is that your models don't know anything about how they're being saved, so you have great flexibility in how you want to persist your data, instead of being tied to the limitations of ActiveRecord.

Best OOP approach for this simple PHP app?

Ok, I couldn't insert the overview image, but I'm required to create a PHP app for my studies, it must allow student to register, and the administrator to edit course and student info.
We are not required to code it in an OOP style, but since its the best programming practice to code in OOP, why not learn OOP from the beginning.
I'm a beginner but I know the basics about OOP, classes, inheritance, setters and getters and all that cool lingo, but I'm struggling to decide which parts of this app should be objects, should I make course and student classes or add, edit and delete classes? Any advice on how to approach and visualize such a problem would be appreciated.
Very roughly: This is how I would do it:
Store your data in SQL or XML. You will need two SQL tables, one for Students and one for Courses. You can use one XML file containing all the data, or you can use two files (which I recommend).
Create a class called, for example, dataItem with a property like '$arr_fields' corresponding to a single data record (a single row in a SQL table, or an XML record).
The dataItem class should have the following methods (all public):
loadFromSQL() (or loadFromXML())
saveToSQL() (or saveToXML())
add(), edit() and delete()
a view() method using HTML
These methods are obviously used to read and write data between the SQL/XML data and $arr_fields of the class, and to display the data in $arr_fields. The keys of $arr_fields are the SQL column names (or XML tag or attribute names) for the specific SQL table.
Try not to call loadFromSQL() or saveToSQL() in your constructor or in any of the other methods which are used to modify only the class data. Keep these actions separate. EDIT: This is a personal preference which helps me to keep track of the state of my objects.
Create Student and Course classes that extends the dataItem class.
You can override methods, for instance the view() method, inside your extended classes if you need to.
Then you can call the methods in Students and Courses from an Admin object (like rcdmk suggested) or maybe from StudentFolder and CourseFolder classes whose view() method contains buttons for the actions that need to be performed. (Let StudentFolder and CourseFolder extend a Folder class that you create).
UPDATE:
For example: If your primary key in a SQL table is id, then dataItem's loadFromSQL($id, $tablename) should set $arr_fields so that its keys are the column names and it's values are the values from the row whose primary value is equal to $id.
In Students, you can then override loadFromSQL() as follows:
class Students extends dataItem {
// other attributes
public function loadFromSQL($id) {
parent::loadFromSQL($id, "Students");
}
}
EDIT: On reconsideration, it might be better to set $arr_fields["id"] = $id and also set $tablename with the constructor for dataItem - then you never have to override loadFromSQL() or specify parameters for it. loadFromSQL() should then load the record if it exists. saveToSQL() should save $arr_fields in SQL if $arr_fields["id"] is set and create a new record if it is not set. Anyway, you must find a consistent way of interacting with the data which works for you, these are just possibilities.
However, if you are not experienced with OOP and SQL or XML, you might be opening a can of worms for yourself and it might be better to just do your assignment using functions only and php arrays for your data. Unless you have some time to learn...
From a simple perspective:
Abstract the main objects as classes and use methods for actions of this objects:
Student (object) are deleted (action) by the Admin (object), so
Admin class will have a deleteStudent method, because Admin deletes Students.
Another aprouch is to concentrate all Student related actions in the Student class:
Student class will have a public delete method that Admin can use.
Anyone that think this in better ways of explanation can edit this wiki.
Think about which aspects of your system are actually objects, you know, something you can do something with. The methods are what you do to the objects. So, you're on the right track with course and student classes, and add, edit, and delete would be methods of those classes.
But don't get too bogged down with it. If it's not your core assignment objective, you could quickly get in over your head by trying to do everything exactly the right way. If you can formulate a clear way to get to where you need to go, then go for it, if it seems confusing, back off it a little and learn some more.
You say that you know the basis of OOP, however you ask whether you should create Course, Student classes OR Add, Delete, Edit classes. Well maybe there are other practices, but I guess the most popular one and the only I am aware of is to use nouns as classes and verbs as their methods. Hence, intuitively there is something wrong with class "Add" or "Edit". What I would have done if I were were, is to think of all "entities" that might be considered an object - like Student, Course, Lecturer, Class (Room) and depending on how advanced your model should be you can add more like Building etc. Then try to implement basic things like creating new student, registering for a course, associating teacher with a course etc. Once you have it in place and IT IS WORKING you might want to add advanced things, like inheritance. For example you might want to say, that both Teacher and Student are a Person so you might want to create such abstract class and use inheritance.

Categories