Design pattern for repetitive switch in getters? - php

I've an ORM model (PHP Active Record), say, for a blogging system. I've something that's a post model that stores the number of likes. The post could either be a picture or quote (say), and they are different tables (and hence models).
The schema is that a post holds data like number of shares, likes, description, etc. along with either a picture or a quote.
So when writing getters for the post model I'm having to write
public function getX() {
if ($this->isPicture()) {
return $this->picture->getX();
}
else if ($this->isQuote()) {
return $this->quote->getX()
}
else {
return self::DEFAULT_X
}
}
I'm currently having to write this structure for many getter. Is there something I can do to avoid that?
PS: Tagged as PHP because that's my code in.
EDIT
Changed comments to code.
This is a model (and a corresponding table in the DB) that has more data than just a picture and quote. Example, description that's part of the post and doesn't reside on either the picture or the quote.
There's tables for pictures and quotes.
Using PHP Active Record and each of the three classes extends the generic model class provided by PHP Active Record.
The picture model has it's own data. Same for quote.

To expand on the idea of the Strategy pattern mentioned in the comments:
class Post {
// get the correct 'strategy'
public function getModel() {
if ($this->isPicture()) {
return $this->picture;
}
if ($this->isQuote()) {
return $this->quote;
}
return null;
}
// using the strategy
public function getX() {
$model = $this->getModel();
if (null === $model) {
return self::DEFAULT_X;
}
return $model->getX();
}
}
Each strategy would presumably implement the same interface as the Post class for exposing those getters. Even better would be to provide a default strategy (rather than returning null) and have that return the default values. That way, the null check in each getter becomes redundant.

An alternative approach to this is a very basic form of metaprogramming. The idea is that you go a level higher than calling your methods by hand, and let the code do it for you.
(Assume that the method definitions are all part of Post)
public function getX($model = null) {
if ($model) return $model->getX();
else return self::DEFAULT_X;
}
// usage
$postModel->getX($pictureModel);
What's happening here is that, in this single instance of getX in your Post model, you're passing in the name of another class, and executing the `getX' method on that instance (if it exists and is callable).
You can extend this in other ways. For example, maybe you don't want to pass an instance in, when the method can do it anyway:
public function getX($model_name = null) {
if ($model_name && $class_exists($model_name) && is_callable(array($model_name, 'getX')) {
$model = new $model_name;
return $model->getX();
} else {
return self::DEFAULT_X;
}
}
// usage
$postModel->getX('Picture');
In this instance, you pass the model in as a string, and the method will do the rest. While this makes it quicker to get what you want, you might find that you don't want to work with fresh instances all the time (or you can't), so there's a bit of a trade-off with this 'convenient' way.
That still doesn't fully solve your problem, though, since you still have to repeat that for each getter, over and over again. Instead, you can try something like this:
public function __call($method, $args) {
$class = $args[0];
if (class_exists($class) && is_callable(array($class, $method))) {
$model = new $class;
return $model->$method();
}
}
// usage
$postModel->getX('Picture');
$postModel->getY('Quote');
$postModel->getZ('Picture');
If you call a function that doesn't exist on the Post model, that magic method will be called, and it'll fire up a new instance of the model name you supply as an argument, and call the getWhatever method on it, if it exists.
It's important to note that you must not define these getters in Post, unless you want to override the methods in the other classes.
There is still the problem of this creating new instances all the time, though, and to remedy this you can use a bit of dependency injection. This means that you let the Post class contains a list of other instances of classes that it wants to use in future, so you can add and remove them at will.
This is what I would consider the actual solution, with the other examples hopefully showing how I've got here (will edit to clarify things, of course).
public $models = array();
public function addModel($instance) {
$this->models[get_class($instance)] = $instance;
}
public function __call($method, $args) {
$class = $args[0];
if (array_key_exists($class, $this->models)) {
$model = $this->models[$class];
if (is_callable(array($model, $method)) {
return $model->$method();
}
}
}
// usage
$this->addModel($pictureModel);
$this->addModel($quoteModel);
$this->getX('Picture');
$this->getY('Quote');
Here, you're passing in your existing instances of models into the Post class, which then stores them in an array, keyed by the name of the class. Then, when you use the class as described in the last example, instead of creating a new instance, it will use the instance it has already stored. The benefit of this is that you might do things to your instances that you'd want reflected in the Post model.
This means that you can add as many new models as you like that need to plug into Post, and the only thing you need to do is inject them with addModel, and implement the getters on those models.
They all require you to tell the class what models to call at some point or another. Since you have an array of dependent models, why not add a way to get everything?
public function __call($method, $args) {
$class = $args[0];
if (array_key_exists($class, $this->models)) {
$model = $this->models[$class];
if (is_callable(array($model, $method)) {
return $model->$method();
}
} elseif ($class === 'all') {
// return an array containing the results of each method call on each model
return array_map(function($model) use ($method) {
if (is_callable(array($model, $method) return $model->$method();
}, $this->models);
}
}
// usage
$postModel->getX('all');
Using this, you'll get an array containing the return values of each getX method on each model you added with addModel. You can create pretty powerful functions and classes that do all this stuff without you having to repeat tedious logic.
I have to mention that these examples are untested, but at the very least I hope the concept of what you can do has been made clear.
Note:
The same thing can be applied to __GET and __SET methods, too, which are used for accessing properties. It's also worth saying that there may be the slight risk of a library already using these magic methods, in which case you'll need to make the code a little more intelligent.

Related

How to implement models factory in frameworks which using active records?

So, in my framework X, let it be Phalcon, I often create models objects.
Let's assume that all fields already validated. Questions related only about creation logic.
A simple example of creating Users object and save it to DB:
<?php
$user = new Users();
$user->setName($name);
$user->setLastName($lastname);
$user->setAge($age);
$user->create();
For simplicity, I show here only 3 fields to setup, in the real world they always more.
I have 3 questions:
1) What the best way to encapsulate this logic in Factory class? If I create Factory class that will create objects like Users object, every time I will need pass long amount of parameters.
Example:
<?php
$factory = new UsersFactory();
$factory->make($name, $lastname, $address, $phone, $status, $active);
2) Even if I implement Factory in a way showed above - should Factory insert data in DB? In my example call method create()? Or just perform all setters operations?
3) And even more, what if i will need to create Users objects with relations, with other related objects?
Thank you for any suggestions.
Your question starts out simple and then builds with complexity. Reading your post it sounds like your concerned about the number of arguments you would have to pass to the method to build the object. This is a reasonable fear as you should try to avoid functions which take more than 2 or 3 args, and because sometimes you will need to pass the 1st 3rd and 5th arg but not the 2nd and 4th which just gets uncomfortable.
I would instead encourage you to look at the builder pattern.
In the end it will not be that much different than just using your User object directly however it will help you prevent having a User object in an invalid state ( required fields not set )
1) What the best way to encapsulate this logic in Factory class? If I create Factory class that will create objects like Users object, every time I will need pass long amount of parameters.
This is why I recommended the builder pattern. To avoid passing a large number of params to a single function. It also would allow you to validate state in the build method and handle or throw exceptions.
class UserBuilder {
protected $data = [];
public static function named($fname, $lname) {
$b = new static;
return $b
->withFirstName($fname)
->withLastName($lname);
}
public function withFirstName($fname) {
$this->data['first_name'] = $fname;
return $this;
}
public function withFirstName($lname) {
$this->data['last_name'] = $lname;
return $this;
}
public function withAge($age) {
$this->data['age'] = $age;
return $this;
}
public function build() {
$this->validate();
$d = $this->data;
$u = new User;
$u->setFirstName($d['first_name']);
$u->setLastName($d['last_name']);
$u->setAge($d['age']);
return $u;
}
protected function validate() {
$d = $this->data;
if (empty($d['age'])) {
throw new Exception('age is required');
}
}
}
then you just do..
$user = UserBuilder::named('John','Doe')->withAge(32);
now instead of the number of function arguments growing with each param, the number of methods grows.
2) Even if I implement Factory in a way showed above - should Factory insert data in DB? In my example call method create()? Or just perform all setters operations?
no it should not insert. it should just help you build the object, not assume what your going to do with it. You may release that once you build it you will want to do something else with it before insert.
3) And even more, what if i will need to create Users objects with relations, with other related objects?
In Phalcon those relationships are part of the entity. You can see in their docs this example:
// Create an artist
$artist = new Artists();
$artist->name = 'Shinichi Osawa';
$artist->country = 'Japan';
// Create an album
$album = new Albums();
$album->name = 'The One';
$album->artist = $artist; // Assign the artist
$album->year = 2008;
// Save both records
$album->save();
So to relate this back to your user example, suppose you wanted to store address information on the user but the addresses are stored in a different table. The builder could expose methods to define the address and the build method would create both entities together and return the built User object which has a reference to the Address object inside it because of how Phalcon models work.
I don't think it's entirely necessary to use a builder or "pattern" to dynamically populate your model properties. Though it is subjective to what you're after.
You can populate models through the constructor like this
$user = new Users([
'name' => $name,
'lastName' => $lastname,
'age' => $age,
]);
$user->create();
This way you can dynamically populate your model by building the array instead of numerous method calls.
It's also worth noting that if you want to use "setters" and "getter" methods you should define the properties as protected. The reason for this is because Phalcon will automatically call the set/get methods if they exist when you assign a value to the protected property.
For example:
class User extends \Phalcon\Mvc\Model
{
protected $name;
public function setName(string $name): void
{
$this->name = $name;
}
public function getName(): string
{
return $this->name;
}
}
$user= new MyModel();
$user->name = 'Cameron'; // This will invoke User::setName
echo $user->name; // This will invoke User::getName
It is also worth noting that the properties will behave as you'd expect a protected property to behave the same as a traditional protected property if the respective method is missing. For example, you cannot assign a value to a protected model property without a setter method.

Decorator case or not?

I have a contract "ArticleStorage" that every storage must be subscribe to be valid for model.
True, this is not the problem, my problem is: pagination ... or "results modification", in this case at fetchAll, i want modify its behavior but without adding parameters, etc
<?php
interface ArticleStorage
{
// public function insert();
// public function update();
// public function delete();
public function fetchAll();
}
class MySQLArticleStorage implements ArticleStorage
{
public function fetchAll()
{
// SELECT * FROM `articles`;
}
}
?>
How my model works.
class ArticlesModel
{
public function __construct(ArticleStorage $storage)
{
}
}
in this case, I expect a "ArticleStorage" but do not know which "Storage" was given, true ... and i want to paginate or apply a results modification, using the Storage.
class MySQLArticleResultsModifier
{
public function __construct(MySQLArticleStorage $storage)
{
}
public function fetchAll()
{
// ...
}
}
In case of a pagination, how i can modify ArticleStorage fetchAll and apply my modified query ?
Is there a case where your model demands that a fetchall on top of another fetchall is possible; I don't think so, infact this is how you decide if you need a decorator or not, by answering this question to yourself
Is the decorator function you are thinking of making works like a decoration{like a real decoration where you can put stars on your christmas tree {decoration1}, and some toys on your tree {decoration2} at the same instance? Otherwise there is no point in making a decorator pattern, The nature of decorator is to decorate the concrete implementations from outside world, and change the output, without being affected by the other decoration being applied to a concrete instance.
Now as to the current implementation, I think #mrhobo is quite right, your fetch function might look like
public function fetch($limit, $order,$sort)
A very smart fetch could also expect the user to send a hashtable of key-value , of the columnname = value of column by using which you can make your own select query on the fly.

Store PHP class settings in variables or return them from methods

I see two different implementations when people handle classes that extend other classes and provide functionality based on certain setting inside the class.
Variables are used to store settings.
Methods are used to return settings.
Using Variables:
class Model {
var $fields = array();
function getFields() {
return array_keys($this->fields);
}
function getRules() {
return $this->fields;
}
}
class Person extends Model {
var $fields = array(
'name' => array('maxLength'=>10),
'email' => array('maxLength'=>50, 'validEmail'=>true),
);
}
Using Methods:
class Model {
function getFields() {}
}
class Person extends Model {
function getFields() {
return array('name','email');
}
function getRules() {
return array(
'name' => array('maxLength'=>10),
'email' => array('maxLength'=>50, 'validEmail'=>true),
);
}
}
Both examples achieve the same results, I can do things like $person->getFields() and $person->getRules(), but in the method-example I don't like the "duplicate" field list, because the fields are actually defined both in $person->getFields() and $person->getRules() and it must compute the array every time it is asked for via the method. On the other hand, I don't like that every object stores all the settings in a variable. It seems like a resource waste. So I'm just wondering what's the better way.
My main questions are:
Is there a performance-reason to pick one way over the other? 2)
Is there a OOP-logic/ease-of-programming/other-reason to pick one
way over the other?
From a few benchmark tests - the times are pretty similar - the exeption though
return array('name','email');
is much faster than
return array_keys($this->fields);
Running 10,000 operations for each method produced these averages:
Variable:
getFields 0.06s
getRules 0.05s
Method:
getFields 0.04s
getRules 0.05s
To answer your second question - it depends on your use-case - if the data stored in these objects is static, or if it will come from another datasource / config file.
One follow up question, why not use object properties?
class Person extends Model {
protected $name
protected $email
public function getName() {
return $this->name;
}
public function getEmail() {
return $this->email;
}
}
My opinion is pick what you are comfortable with, there is no much performance loss or performance gain from using either. You better save the performance saving effort for data handling.
For me I use object properties, it looks clear when you are looking at the class, for storing such default properties, and if you want to override them, then use this beautiful syntax:
array()+array()

PHP Lazy loading objects and dependency injection

I have recently started reading about dependency injection and it has made me rethink some of my designs.
The problem i have is something like this:
Let's say i have two classes: Car and Passenger;
For those two classes i have some data mappers to work with the database: CarDataMapper and PassengerDataMapper
I want to be able to do something like this in code:
$car = CarDataMapper->getCarById(23); // returns the car object
foreach($car->getPassengers() as $passenger){ // returns all passengers of that car
$passenger->doSomething();
}
Before I knew anything about DI, I would build my classes like this:
class Car {
private $_id;
private $_passengers = null;
public function getPassengers(){
if($this->_passengers === null){
$passengerDataMapper = new PassengerDataMapper;
$passengers = $passengerDataMapper->getPassengersByCarId($this->getId());
$this->setPassengers($passengers);
}
return $this->_passengers;
}
}
I would also have similar code in the Passenger->getCar() method to fetch the car the passenger is in.
I now understand that this creates dependencies (well, I understood it before too, but I wasn't aware that this is "wrong") between the Car and the Passenger objects and the data mapper objects.
While trying to think of the solution for this two options came to mind, but I don't really like any of them:
1: Doing something like this:
$car = $carDataMapper->getCarById(23);
$passengers = $passengerDataMapper->getPassengersByCarId($car->getId());
$car->setPassengers($passengers);
foreach($car->getPassengers() as $passenger){
$passenger->doSomething();
}
But what if passengers have objects that they need injected, and what if the nesting goes to ten or twenty levels... I would wind up instantiating nearly every object in the start of my application, which would in turn query the entire database during the process.
If i have to send the passenger to another object which has to do something with the objects that the passenger holds, I do not want to immediately instantiate these objects too.
2: Injecting the data mappers into the car and passenger objects and having something like this:
class Car {
private $_id;
private $_passengers = null;
private $_dataMapper = null;
public function __construct($dataMapper){
$this->setDataMapper($dataMapper);
}
public function getPassengers(){
if($this->_passengers === null && $this->_dataMapper instanceof PassengerDataMapper){
$passengers = $this->_dataMapper->getPassengersByCarId($this->getId());
$this->setPassengers($passengers);
}
return $this->_passengers;
}
}
I dont like this any better, because it's not like the Car is really unaware of the data mapper, and without the data mapper, the Car could behave unpredictably (not returning passengers, when it actually has them)
So my first question is:
Am I taking a completely wrong approach here, because, the more I look at it, the more it looks like I'm building an ORM, instead of a business layer?
The second question is:
is there a way of actually decoupling the objects and the data mappers in a way that would allow me to use the objects as described in the very first code block?
Third question:
I've seen some answers for other languages (some version of C, I think) resolving this issue with something like this described here:
What is the proper way to inject a data access dependency for lazy loading?
As I haven't had time to play with other languages, this makes no sense to me, so I'd be grateful if someone would explain the examples in the link in PHP-ish.
I have also looked at some DI frameworks, and read about DI Containers and Inversion of Control, but from what I understood they are used to define and inject dependencies for 'non dynamic' classes, where for instance, the Car would depend on the Engine, but it would not need the engine to be loaded dynamically from the db, it would simply be instantiated and injected into the Car.
Sorry for the lengthy post and thanks in advance.
Maybe off-topic, but I think that it will help you a bit:
I think that you try to achieve the perfect solution. But no matter what you come up with, in a couple of years, you will be more experienced and you'll definitely be able to improve your design.
Over the past years with my colleagues we had developed many ORMs / Business Models, but for almost every new project we were starting from scratch, since everyone was more experienced, everyone had learned from the previous mistakes and everyone had come across with new patterns and ideas. All that added an extra month or so in development, which increased the cost of the final product.
No matter how good the tools are, the key problem is that the final product must be as good as possible, at the minimum cost. The client won't care and won't pay for things that can't see or understand.
Unless, of course, you code for research or for fun.
TL;DR: Your future self will always outsmart your current self, so do not overthink about it. Just pick carefully a working solution, master it and stick with it until it won't solve your problems :D
To answer your questions:
Your code is perfectly fine, but the more you will try to make it "clever" or "abstract" or "dependency-free", the more you will lean towards an ORM.
What you want in the first code block is pretty feasible. Take a look at how the Doctrine ORM works, or this very simple ORM approach I did a few months ago for a weekend project:
https://github.com/aletzo/dweet/blob/master/app/models
I was going to say "I know this is an old question but..." then I realized you posted it 9 hours ago, which is cool, because I just came to a satisfactory 'resolution' for myself. I thought of the implementation and then I realized it is what people were calling 'dependency injection'.
Here is an example:
class Ticket {
private $__replies;
private $__replyFetcher;
private $__replyCallback;
private $__replyArgs;
public function setReplyFetcher(&$instance, $callback, array $args) {
if (!is_object($instance))
throw new Exception ('blah');
if (!is_string($callback))
throw new Exception ('blah');
if (!is_array($args) || empty($args))
throw new Exception ('blah');
$this->__replyFetcher = $instance;
$this->__replyCallback = $callback;
$this->__replyArgs = $args;
return $this;
}
public function getReplies () {
if (!is_object($this->__replyFetcher)) throw new Exception ('Fetcher not set');
return call_user_func_array(array($this->__replyFetcher,$this->__replyCallback),$this->__replyArgs);
}
}
Then, in your service layer (where you 'coordinate' actions between multiple mappers and models) you can call the 'setReplyFetcher' method on all of the ticket objects before you return them to whatever is invoking the service layer -- OR -- you could do something very similar with each mapper, by giving the mapper a private 'fetcherInstance' and 'callback' property for each mapper the object is going to need, and then set THAT up in the service layer, then the mapper will take care of preparing the objects. I am still weighing the differences between the two approaches.
Example of coordinating in the service layer:
class Some_Service_Class {
private $__mapper;
private $__otherMapper;
public function __construct() {
$this->__mapper = new Some_Mapper();
$this->__otherMapper = new Some_Other_Mapper();
}
public function getObjects() {
$objects = $this->__mapper->fetchObjects();
foreach ($objects as &$object) {
$object->setDependentObjectFetcher($this->__otherMapper,'fetchDependents',array($object->getId()));
}
return $objects;
}
}
Either way you go, the object classes are independent of mapper classes, and mapper classes are independent of each other.
EDIT: Here is an example of the other way to do it:
class Some_Service {
private $__mapper;
private $__otherMapper;
public function __construct(){
$this->__mapper = new Some_Mapper();
$this->__otherMapper = new Some_Other_Mapper();
$this->__mapper->setDependentFetcher($this->__otherMapper,'someCallback');
}
public function fetchObjects () {
return $this->__mapper->fetchObjects();
}
}
class Some_Mapper {
private $__dependentMapper;
private $__dependentCallback;
public function __construct ( $mapper, $callback ) {
if (!is_object($mapper) || !is_string($callback)) throw new Exception ('message');
$this->__dependentMapper = $mapper;
$this->__dependentCallback = $callback;
return $this;
}
public function fetchObjects() {
//Some database logic here, returns $results
$args[0] = &$this->__dependentMapper;
$args[1] = &$this->__dependentCallback;
foreach ($results as $result) {
// Do your mapping logic here, assigning values to properties of $object
$args[2] = $object->getId();
$objects[] = call_user_func_array(array($object,'setDependentFetcher'),$args)
}
}
}
As you can see, the mapper requires the other resources to be available to even be instantiated. As you can also see, with this method you are kind of limited to calling mapper functions with object ids as parameters. I'm sure with some sitting down and thinking there is an elegant solution to incorporate other parameters, say fetching 'open' tickets versus 'closed' tickets belonging to a department object.
Here is another approach I thought of. You can create a 'DAOInjection' object that acts as a container for the specific DAO, callback, and args needed to return the desired objects. The classes then only need to know about this DAOInjection class, so they are still decoupled from all of your DAOs/mappers/services/etc.
class DAOInjection {
private $_DAO;
private $_callback;
private $_args;
public function __construct($DAO, $callback, array $args){
if (!is_object($DAO)) throw new Exception;
if (!is_string($callback)) throw new Exception;
$this->_DAO = $DAO;
$this->_callback = $callback;
$this->_args = $args;
}
public function execute( $objectInstance ) {
if (!is_object($objectInstance)) throw new Exception;
$args = $this->_prepareArgs($objectInstance);
return call_user_func_array(array($this->_DAO,$this->_callback),$args);
}
private function _prepareArgs($instance) {
$args = $this->_args;
for($i=0; $i < count($args); $i++){
if ($args[$i] instanceof InjectionHelper) {
$helper = $args[$i];
$args[$i] = $helper->prepareArg($instance);
}
}
return $args;
}
}
You can also pass an 'InjectionHelper' as an argument. The InjectionHelper acts as another callback container -- this way, if you need to pass any information about the lazy-loading object to its injected DAO, you won't have to hard-code it into the object. Plus, if you need to 'pipe' methods together -- say you need to pass $this->getDepartment()->getManager()->getId() to the injected DAO for whatever reason -- you can. Simply pass it like getDepartment|getManager|getId to the InjectionHelper's constructor.
class InjectionHelper {
private $_callback;
public function __construct( $callback ) {
if (!is_string($callback)) throw new Exception;
$this->_callback = $callback;
}
public function prepareArg( $instance ) {
if (!is_object($instance)) throw new Exception;
$callback = explode("|",$this->_callback);
$firstCallback = $callback[0];
$result = $instance->$firstCallback();
array_shift($callback);
if (!empty($callback) && is_object($result)) {
for ($i=0; $i<count($callback); $i++) {
$result = $result->$callback[$i];
if (!is_object($result)) break;
}
}
return $result;
}
}
To implement this functionality in the object, you would require the injections at construction to ensure that the object has or can get all of the information it needs. Each method that uses an injection simply calls the execute() method of the respective DAOInjection.
class Some_Object {
private $_childInjection;
private $_parentInjection;
public function __construct(DAOInjection $childInj, DAOInjection $parInj) {
$this->_childInjection = $childInj;
$this->_parentInjection = $parInj;
}
public function getChildObjects() {
if ($this->_children == null)
$this->_children = $this->_childInjection->execute($this);
return $this->_children;
}
public function getParentObjects() {
if ($this->_parent == null)
$this->_parent = $this->_parentInjection->execute($this);
return $this->_parent;
}
}
I would then, in the constructor of my service class, instantiate the mappers relevant to that service using the relevant DAOInjection classes as arguments for the mappers' constructors. The mappers would then take care of making sure each object has its injections, because the mapper's job is to return complete objects and handle the saving/deleting of objects, while the service's job is to coordinate the relationships between various mappers, objects, and so on.
Ultimately you can use it to inject callbacks to services OR mappers, so say you want your 'Ticket' object to retrieve a parent user, which happens to be outside the realm of the 'Ticket Service' -- the ticket service can just inject a callback to the 'User Service', and it won't have to know a thing about how the DAL works for other objects.
Hope this helps!

Am I writing procedural code with objects or OOP?

So basically I'm making a leap from procedural coding to OOP.
I'm trying to implement the principles of OOP but I have a nagging feeling I'm actually just writing procedural style with Objects.
So say I have a list of pipes/chairs/printers/whatever, they are all all listed as products in my single table database. I need to build a webapp that displays the whole list and items depending on their type, emphasis is on 'correct' use of OOP and its paradigm.
Is there anything wrong about just doing it like:
CLass Show
{
public function showALL(){
$prep = "SELECT * FROM myProducts";
$q = $this->db-> prepare($prep);
$q->execute();
while ($row = $q->fetch())
{
echo "bla bla bla some arranged display".$row['something']
}
}
and then simply
$sth = new show();
$sth->showAll();
I would also implement more specific display methods like:
showSpecificProduct($id)->($id would be passed trough $_GET when user say clicks on one of the links and we would have seperate product.php file that would basically just contain
include('show.class.php');
$sth = new show();
$sth->showSpecificProduct($id);
showSpecificProduct() would be doing both select query and outputing html for display.
So to cut it short, am I going about it allright or I'm just doing procedural coding with classes and objects. Also any ideas/hints etc. on resolving it if I'm doing it wrong?
As well as the model practices described by #Phil and #Drew, I would urge you to separate your business, data and view layers.
I've included a very simple version which will need to be expanded upon in your implementation, but the idea is to keep your Db selects separate from your output and almost "joining" the two together in the controller.
class ProductController
{
public $view;
public function __construct() {
$this->view = new View;
}
public function indexAction() {
$model = new DbProductRepository;
$products = $model->fetchAll();
$this->view->products = $products;
$this->view->render('index', 'product');
}
}
class View
{
protected $_variables = array();
public function __get($name) {
return isset($this->_variables['get']) ? $this->_variables['get'] : null;
}
public function __set($name, $value) {
$this->_variables[$name] = $value;
}
public function render($action, $controller) {
require_once '/path/to/views/' . $controller . '/' . $action . '.php';
}
}
// in /path/to/views/product/index.php
foreach ($this->products as $product) {
echo "Product ID {$product['id']} - {$product['name']} - {$product['cost']}<br />\n";
}
A better fit would be to implement a repository pattern. An example interface might be
interface ProductRepository
{
public function find($id);
public function fetchAll();
}
You would then create a concrete implementation of this interface
class DbProductRepository implements ProductRepsoitory
{
private $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
public function find($id)
{
// prepare execute SQL statement
// Fetch result
// return result
}
public function fetchAll()
{
// etc
}
}
It's generally a bad idea to echo directly from a method or function. Have your methods return the appropriate objects / arrays / whatever and consume those results.
The scenario you are describing above seems like a good candidate for MVC.
In your case, I would create a class strictly for accessing the data (doing selects of product categories or specific products) and then have a different file (your view) take the output and display it.
It could look something like this:
class Product_Model {
public function find($prodId) { ... }
public function fetchAll($category = '') { ... }
public function search($string) { ... }
}
Then somewhere else you can do:
$products = new Product_Model();
$list = $products->fetchAll(37); // get all from category 37
// in true MVC, you would have a view that you would assign the list to
// $view->list = $list;
foreach($ilst as $product) {
echo "Product ID {$product['id']} - {$product['name']} - {$product['cost']}<br />\n";
}
The basic principle of MVC is that you have model classes that are simply objects representing data from some data source (e.g. database). You might have a mapper that maps data from the database to and from your data objects. The controller would then fetch the data from your model classes, and send the information to the view, where the actual presentation is handled. Having view logic (html/javascript) in controllers is not desirable, and interacting directly with your data from the controller is the same.
first, you will want to look into class autoloading. This way you do not have to include each class you use, you just use it and the autoloader will find the right file to include for you.
http://php.net/manual/en/language.oop5.autoload.php
each class should have a single responsibility. you wouldn't have a single class that connects to the database, and changes some user data. instead you would have a database class that you would pass into the user class, and the user class would use the database class to access the database. each function should also have a single responsibility. you should never have an urge to put an "and" in a function name.
You wouldn't want one object to be aware of the properties of another object. this would cause making changes in one class to force you to make changes in another and it eventually gets difficult to make changes. properties should be for internal use by the object.
before you start writing a class, you should first think about how you would want to be able to use it (see test driven development). How would you want the code to look while using it?
$user = new User($db_object);
$user->load($id);
$user->setName($new_name);
$user->save();
Now that you know how you want to be able to use it, it's much easier to code it the right way.
research agile principles when you get a chance.
One rule of thumb is that class names should usually be nouns, because OOP is about having software objects that correspond to real conceptual objects. Class member functions are usually the verbs, that is, the actions you can do with an object.
In your example, show is a strange class name. A more typical way to do it would be to have a class called something like ProductViewer with a member function called show() or list(). Also, you could use subclasses as a way to get specialized capabilities such as custom views for particular product types.

Categories