Sorry for perhaps not the most accurate title. I'm having trouble figuring out what this would even be called.
I'm somewhat new to OOP with php, as most of my time is spent with procedural programming. In an effort to better learn these concepts, I'm taking an existing application and rewriting portions of it using OOP. The below example is how I set up the base class, and then I extended the base class into several smaller classes for easier maintainability. Below, you can see how I extended the base class to create a user class. Please note, that my class definitions are in separate files, but I have a working autoloader that automatically registers them
class EventScheduler{
function __construct(){
// set up database connections here
}
}
class User extends EventScheduler{
private function getUserProfile($username){
// return an array here representing
// details of passed username from database
}
public function getUserType($username){
return $this->getUserProfile($username)['user_type'];
}
}
What I'd like to be able to do is reference the User class from inside the base class, like this:
$eventApp = new EventScheduler();
$userType = $eventApp->User->getUserProfile("nameHere");
What I'm currently doing is this:
$eventApp = new EventScheduler();
//do some stuff here using the base class
$users = new User();
$userType = $users->getUserProfile("nameHere");
But as I add more child classes, I don't want to have to instantiate every extended class like I did there, I'd like to have them all grouped under the base object, rather than having each extended class in it's own object.
What you want to do is
$users = new User();
//do some stuff here using the base class
// ie $users->someFunctionFromEventScheduler();
$userType = $users->getUserProfile("nameHere");
This is a good reason to start reading up on common design patterns in OOP. There are plenty of good resources for this online and a quick google search will yield plenty of results and examples mostly hosted on github.
The specific pattern I believe you are looking for is the mediator pattern (Mediator pattern example in PHP). Rather than extending a class, as you are doing in your example, the mediator pattern is useful when you want an instance of a class that has access to many other classes that can all communicate with each other through one base class. The premise is that 'One good friend is better than many acquaintances.`.
An example for you: (interfaces are incredibly useful here as they define specific characteristics that are required in each of the classes)
/**
* Interface Mediator
*/
interface Mediator {
/**
* #param string $key
* #param Mediated $mediated
* #return void
*/
public function attach($key, Mediated $mediated);
/**
* #param $key
* #return Mediated
*/
public function getAttached($key);
}
/**
* Interface Mediated
*/
interface Mediated {
/**
* #param Mediator $mediator
* #return void
*/
public function setMediator(Mediator $mediator);
/**
* #return Mediator
*/
public function getMediator();
}
Now we need a base mediator class, I'll use your event scheduler example. Notice that it implements the Mediator interface and must, as a result implement the methods the interface requires.
/**
* Class EventScheduler
*/
class EventScheduler implements Mediator {
/**
* A collection of mediated instances.
*
* #var array
*/
protected $mediated = [];
/**
* #param string $key
* #param Mediated $mediated
* #return void
*/
public function attach($key, Mediated $mediated)
{
// So upon attaching a mediated instance we can build the two
// way binding in one place using the key as the identifier.
// First we set $this on the mediated instance.
$mediated->setMediator($this);
// Then we add this instance to our mediated array inside this instance
$this->mediated[$key] = $mediated;
}
/**
* #param $key
* #return Mediated
*/
public function getAttached($key)
{
return $this->mediated[$key];
}
}
Now we can setup a mediated instance. That can be attached to the mediator. Notice it implements the Mediated interface.
/**
* Class User
*/
class User implements Mediated {
/**
* #var Mediator
*/
protected $mediator;
/**
* #param Mediator $mediator
* #return void
*/
public function setMediator(Mediator $mediator)
{
$this->mediator = $mediator;
}
/**
* #return Mediator
*/
public function getMediator()
{
return $this->mediator;
}
}
You can create as many of the mediated instances as you like and attach them to the Mediator instance. Bear in mind that this isn't a specific mediator, in that many instances can be attached, in most cases it's better to be explicit with which classes can be attached rather than allowing dynamic registration by a key.
$scheduler = new EventScheduler();
$user = new User();
$scheduler->attach('user', $user);
// Now we know that we can get the mediator from the User class
$user->getMediator();
// We can also get the User from the mediator itself.
$scheduler->getAttached('user');
As you attach more classes you'll notice that each of them can use their mediator to get instances of the other attached classes, this is where the concept of one good friend comes from.
This is just an example and not fully featured for brevity, but should give you a good idea why using common design patterns will help you enormously and is a very good place to start when learning OOP if you want to develop good habits.
Related
I've noted that for creating a facade class, laravel provides only name "db"
framework/src/Illuminate/Support/Facades/DB.php
class DB extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'db';
}
}
I looked deeper and figured out that this method uses the provided name
framework/src/Illuminate/Support/Facades/Facade.php
protected static function resolveFacadeInstance($name)
{
if (is_object($name)) {
return $name;
}
if (isset(static::$resolvedInstance[$name])) {
return static::$resolvedInstance[$name];
}
return static::$resolvedInstance[$name] = static::$app[$name];
}
I understand first and second If statements.
But I have problems with understanding this:
return static::$resolvedInstance[$name] = static::$app[$name]
As I understood that $app is a protected property of Facade class which contains an instance of \Illuminate\Contracts\Foundation\Application class.
/**
* The application instance being facaded.
*
* #var \Illuminate\Contracts\Foundation\Application
*/
protected static $app;
My two questions:
How is it possible to use an object as an array(static::$app[$name]) if Application class doesn't extends ArrayObject class?
How laravel understands which class to call with providing only a short name 'db'?
Clicking through the Laravel source, I found this. As you can see, ApplicationContract (the private static $app from your question) is implemented by Application. This is in turn derived from Container, which implements the PHP core ArrayAccess interface. Carefully implementing this whole chain eventually makes Applicatin accessible like an array.
Turns out it boils down to good ole' object oriented programming :)
// Illuminate/Foundation/Application.php
class Application extends Container implements ApplicationContract, HttpKernelInterface
^^^^^^^^^ ^-> the private static $app in your question.
// Illuminate/Container/Container.php
class Container implements ArrayAccess, ContainerContract
^^^^^^^^^^^
// PHP core ArrayAccess documentation
/**
* Interface to provide accessing objects as arrays.
* #link http://php.net/manual/en/class.arrayaccess.php
*/
interface ArrayAccess {
You can look this, php manual and use ArrayAccess interface:
http://php.net/manual/en/class.arrayaccess.php
I have Entities and Repositories in my project. To simplify, I have
EntityInterface
UserEntity
BusinessEntity
Interface:
interface Entity
{
/**
* #return EntityId
*/
public function getId();
}
Implementations
class UserEntity implements Entity
{
/**
* #return EntityId
*/
public function getId(){
//...do something here for return
return $userId;
}
}
and
class BusinessEntity implements Entity
{
/**
* #return EntityId
*/
public function getId(){
//...do something here for return
return $userId;
}
}
I would like to define a Repository base-functionality, like save, so my interface looks like:
interface Repository
{
/**
* #param Entity $entity
*
* #throws \InvalidArgumentException If argument is not match for the repository.
* #throws UnableToSaveException If repository can't save the Entity.
*
* #return Entity The saved entity
*/
public function save(Entity $entity);
}
Later, I have different interfaces for different type of Repositories, like UserRepository and BusinessRepository
interface BusinessRepository extends Repository
{
/**
* #param BusinessEntity $entity
*
* #throws \InvalidArgumentException If argument is not match for the repository.
* #throws UnableToSaveException If repository can't save the Entity.
*
* #return Entity The saved entity
*/
public function save(BusinessEntity $entity);
}
The above code fails, because Declaration must be compatible with Repository...
however BusinessEntity implements Entity, so it's compatible.
I have many type of entities, so If I can't type-hint, I always need to check, that the passed instance is instanceof what I need. It's stupid.
The following code fails again:
class BusinessRepository implements Repository
{
public function save(BusinessEntity $entity)
{
//this will fail, however BusinessEntity is an Entity
}
}
In general, method parameters have to be contravariant with respect to an inheritance hierarchy or invariant. This means that indeed BusinessEntity would not be "compatible" with Entity when used as a type for a method parameter.
Think of it from a "contract" point of view. Your interface Repository promises that its method save can handle arguments of type Entity. Subtypes inheriting from Repository should be bound to this introduced contract (because otherwise, what sense would it make to define types in the first place, if you cannot be sure what they promises to be able to do?).
Now, if a subtype all of a sudden only accepts more special types, like BusinessEntity, but no longer Entity, the contract's broken. You cannot use BusinessRepository as Repository any more, because you cannot call save with an Entity.
This is counterintuitive at first, but have a look at this: https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Contravariant_method_argument_type
Notice the inheritance arrow in the image.
What's to do? Get rid of the idea of inheritance being the holy grail in object oriented programming. Most of the time, it is not, and introduces all kinds of nasty coupling. Favor composition over inheritance, for example. Have a look at Parameter type covariance in specializations.
It fails because you declare methods that takes different arguments in interfaces. There is also question if there is any different logic in saving BusinessEntity than Entity. I think it shouldn't be. So you can omit save function in business entity and save just work on Entity and should know that Entity has "save" method.
The other way is to use factory pattern or abstract factory over inheritance.
I'm developing a quite complex logistics management system which will keep growing into several other ERP related modules. Therefore, I am trying to have as much of the SRP and Open/Close Principles in place for ease of extension and domain based management.
Therefore, I decided to use Laravel and the following pattern (not sure if this has a name or not):
I will use the PRODUCT object for my example.
An object/entity/domain has a Class
class ProductService {}
This class has a Service Provider which is included in the providers array and is also autoloaded:
ProductServiceServiceProvider
The service provider instantiate (makes) the ProductRepository which is an interface.
The interface currently has a MySQL (and some Eloquent) called EloquentProductRepository implementation(s) and a ProductRepositoryServiceProvider binds the implementation which is also loaded and in the providers array.
Now a product has many different attributes and relationships with other domains and because the other domains (or entities) need to be fully detached and again abiding with the above principle (SRP etc..) I decided to also have the same structure for them as i do for the product...I know some might think that this is too much but we need to have the system very extendable and to be honest I like to be organised and have a uniform pattern (it doesn't take that much more time and saves me a lot later).
My question is this. The ProductService which handles all the business logic of the Product and makes the "Product" what it is will have several dependencies injected on creation of it's instance through the constructor.
This is what it has at the moment:
namespace Ecommerce\Services\Product;
use Ecommerce\Repositories\Product\ProductRepository;
use Ecommerce\Services\ShopEntity\ShopEntityDescriptionService;
use Content\Services\Entity\EntitySeoService;
use Content\Services\Entity\EntitySlugService;
use Ecommerce\Services\Tax\TaxService;
use Ecommerce\Services\Product\ProductAttributeService;
use Ecommerce\Services\Product\ProductCustomAttributeService;
use Ecommerce\Services\Product\ProductVolumeDiscountService;
use Ecommerce\Services\Product\ProductWeightAttributeService;
use Ecommerce\Services\Product\ProductDimensionAttributeService;
/**
* Class ProductService
* #package Ecommerce\Services\Product
*/
class ProductService {
/**
* #var ProductRepository
*/
protected $productRepo;
/**
* #var ShopEntityDescriptionService
*/
protected $entityDescription;
/**
* #var EntitySeoService
*/
protected $entitySeo;
/**
* #var EntitySlugService
*/
protected $entitySlug;
/**
* #var TaxService
*/
protected $tax;
/**
* #var ProductAttributeService
*/
protected $attribute;
/**
* #var ProductCustomAttributeService
*/
protected $customAttribute;
/**
* #var ProductVolumeDiscountService
*/
protected $volumeDiscount;
/**
* #var ProductDimensionAttributeService
*/
protected $dimension;
/**
* #var ProductWeightAttributeService
*/
protected $weight;
/**
* #var int
*/
protected $entityType = 3;
public function __construct(ProductRepository $productRepo, ShopEntityDescriptionService $entityDescription, EntitySeoService $entitySeo, EntitySlugService $entitySlug, TaxService $tax, ProductAttributeService $attribute, ProductCustomAttributeService $customAttribute, ProductVolumeDiscountService $volumeDiscount, ProductDimensionAttributeService $dimension, ProductWeightAttributeService $weight)
{
$this->productRepo = $productRepo;
$this->entityDescription = $entityDescription;
$this->entitySeo = $entitySeo;
$this->entitySlug = $entitySlug;
$this->tax = $tax;
$this->attribute = $attribute;
$this->customAttribute = $customAttribute;
$this->volumeDiscount = $volumeDiscount;
$this->dimension = $dimension;
$this->weight = $weight;
}
`
Is it bad practice to have as much arguments passed to the constructor in PHP (please ignore the long names of the services as these might change when the ERP namespaces have been decided upon)?
As answered by Ben below, in this case it is not. My question was not related to OOP but more to performance etc.. The reason being is that this particular class ProductService is what web deves would do with a controller, i.e. they would probably (and against principles) add all DB relationships in one ProductController which handles repository services (db etc..) and attaches relationships and then it suddenly becomes your business logic.
In my application (and I see most applications this way), the web layer is just another layer. MVC takes care of the web layer and sometimes other Apis too but I will not have any logic except related to views and JS frameworks in my MVC. All of this is in my software.
In conclusion: I know that this is a very SOLID design, the dependencies are injected and they really are dependencies (i.e. a product must have tax and a product does have weight etc..) and they can easily be swapped with other classes thanks to the interfaces and ServiceProviders. Now thanks to the answers, I also know that it is Okay to inject so many dependencies in constructor.
I will eventually write an article about the design patterns which I use and why I use them in different scenarios so follow me if you're interested in such.
Thanks everyone
Generally, no, It's not a bad practice, in most cases. But in your case, as said in the comments by #zerkms, it looks like your class is depending on a lot of dependencies, and you should look into it, and think on how to minimize the dependencies, but if you're actually using them and they should be there, I don't see a problem at all.
However, you should be using a Dependency Injection Container (DIC).
An dependency injection container, is basically a tool which creates the class by the namespace you provide, and it creates the instance including all the dependencies. You can also share objects, so it won't create a new instance of it while creating the dependencies.
I suggest you to ue Auryn DIC
Usage:
$provider = new Provider();
$class = $provider->make("My\\App\MyClass");
What happens here is this:
namespace My\App;
use Dependencies\DependencyOne,
Dependencies\DependencyTwo,
Dependencies\DependencyThree;
class MyClass {
public function __construct(DependencyOne $one, Dependency $two, DependencyThree $three) {
// .....
}
}
Basically, the Provider#make(namespace) creates an instance of the given namespace, and creates the needed instances of it's consturctor's parameters and all parameter's constructors parameters and so on.
I need to cache some info about a user who is logged in (such as security groups, name, username, etc.)
Currently I have a separate class to achieve just this, lets call it CurrentUserHelper. Given a user object, it will cache the appropriate data and save store info in the $_SESSION variable.
The issue is I'm finding a bunch of classes relying just on CurrentUserHelper because they only need a couple of common fields. In fact, most of the functions have the same name as my User class. There's a couple of functions in CurrentUserHelper, such as getSecurityGroupsNames(), that contains a cache of all security group names, but there is no reason this function name could not be in the user class also.
Instead, should I create a CachedUser class and pass this around? This class can extend User, but then I can override getName(), getSecurityGroups(), etc, and returned the cached data, and not preform db requests to get the data.
The downside of passing around a CachedUser object is that this kind of hides the fact the data isn't really up to date if a constructor/function is accepting a type User. I also need to find way to handle merging the entity with Doctrine 2, and making sure entities associating themselves with a CachedUser won't break. If I decide to cache some temporary data (such as # of page views since logged in), this property shouldn't be part of the User class, it's more about the current user's session.
If I continue using the CurrentUserHelper class, maybe I should create an interface and have both CurrentUserHelper and User for the common functionality the two classes would share?
Preface: Extension isn't the best way for these sorts of things.. I'm sure you've heard composition over inheritance shouted at you over and over again. In fact, you can even gain inheritance without using extends!
This sounds like a good use-case for the decorator pattern. Basically, you wrap your existing object with another one that implements the same interface, so it has the same methods as the inner object, but this object's method adds the extra stuff around the method call to the inner object, like caching, for example.
Imagine you have a UserRepository:
/**
* Represents an object capable of providing a user from persistence
*/
interface UserProvider
{
/**
* #param int $id
*
* #return User
*/
public function findUser($id);
}
/**
* Our object that already does the stuff we want to do, without caching
*/
class UserRepository implements UserProvider
{
/**
* #var DbAbstraction
*/
protected $db;
/**
* #var UserMapper
*/
protected $mapper;
/**
* #param DbAbstraction $db
* #param UserMapper $mapper
*/
public function __construct(DbAbstraction $db, UserMapper $mapper)
{
$this->db = $db;
$this->mapper = $mapper;
}
/**
* {#inheritDoc}
*/
public function findUser($id)
{
$data = $this->db->find(['id' => $id]);
/** Data mapper pattern - map data to the User object **/
$user = $this->mapper->map($data);
return $user;
}
}
The above is a really simple example. It'll retrieve the user data from it's persistence (a database, filesystem, whatever), map that data to an actual User object, then return it.
Great, so now we want to add caching. Where should we do this, within the UserRepository? No, because it's not the responsibility of the repository to perform caching, even if you Dependency Inject a caching object (or even a logger!).. this is where the decorator pattern would come in useful.
/**
* Decorates the UserRepository to provide caching
*/
class CachedUserRepository implements UserProvider
{
/**
* #var UserRepository
*/
protected $repo;
/**
* #var CachingImpl
*/
protected $cache;
/**
* #param UserRepository $repo
*/
public function __construct(UserRepository $repo, CachingImpl $cache)
{
$this->repo = $repo;
$this->cache = $cache;
}
/**
* {#inheritDoc}
*
* So, because this class also implements UserProvider, it has to
* have the same method in the interface. We FORWARD the call to
* the ACTUAL user provider, but put caching AROUND it...
*/
public function findUser($id)
{
/** Has this been cached? **/
if ($this->cache->hasKey($id))
{
/**
* Returns your user object, or maps data or whatever
*/
return $this->cache->get($id);
}
/** Hasn't been cached, forward the call to our user repository **/
$user = $this->repo->findUser($id);
/** Store the user in the cache for next time **/
$this->cache->add($id, $user);
return $user;
}
}
Very simply, we've wrapped the original object and method call with some additional caching functionality. The cool thing about this is that, not only can you switch out this cached version for the non-cached version at any time (because they both rely on the same interface), but you can remove the caching completely as a result, just by changing how you instantiate this object (you could take a look at the factory pattern for that, and even decide which factory (abstract factory?) depending on a configuration variable).
Shall I create a separate Zend Application for the user backend of a web application?
My main concern is that I have to have a separate Zend_Auth on both the public website (for clients to login) and for employees to manage the site.
Since it appears to me that I can't use multiple Zend_Auth instances in one application this would be the only solution.
The next concern would be that the two Zend_Auth sessions will collide since they run on the same webspace?
Cheers
Actually, Benjamin Cremer's solution won't work, because Zend_Auth_Admin extends a Singleton implementation, so its getInstance() would yield a Zend_Auth instance, not a Zend_Auth_Admin one.
I myself was confronted with this situation, and seeing that the ZF people (at least in ZF1) see authetication as a single entry-point in an application (they could've made it so that Zend_Auth could contain multiple instances, using LSB in php etc.), made a minor modification to Benjamin Cremer's code - you must also override the getInstance():
<?php
class AdminAuth extends Zend_Auth
{
/**
* #var AdminAuth
*/
static protected $_adminInstance;
/**
* #return Zend_Auth_Storage_Interface
*/
public function getStorage()
{
if (null === $this->_storage) {
$this->setStorage(new Zend_Auth_Storage_Session('Zend_Auth_Admin'));
}
return $this->_storage;
}
/**
* Singleton pattern implementation.
*
* #return AdminAuth
*/
public static function getInstance()
{
if (null === self::$_adminInstance) {
self::$_adminInstance = new self();
}
return self::$_adminInstance;
}
}
Zend_Auth implements the Singleton Pattern so there can only exist one instance of this class.
To distinguish whether the current identity is an admin or an user you could use an isAdmin-Flag, or even better implement the Zend_Acl_Role_Interface.
If it is really required by your application to have two Auth-Sessions at the same time (one for a User, on for an Admin) you could 'copy' the Zend_Auth class by extending it and adjust the session storage.
<?php
class Zend_Auth_Admin extends Zend_Auth
{
/**
* Returns the persistent storage handler
*
* Session storage is used by default unless a different storage adapter has been set.
*
* #return Zend_Auth_Storage_Interface
*/
public function getStorage()
{
if (null === $this->_storage) {
$namespace = 'Zend_Auth_Admin'; // default is 'Zend_Auth'
/**
* #see Zend_Auth_Storage_Session
*/
require_once 'Zend/Auth/Storage/Session.php';
$this->setStorage(new Zend_Auth_Storage_Session($namespace));
}
return $this->_storage;
}
}
So you can use two distinct Auth objects for your Session handling
Zend_Auth::getInstance(); // instance for users
Zend_Auth_Admin::getInstance(); // instance for admins