how does this construct method in each class is working? - php

I am currently working in laravel 5.3. we are following this approach : controller ->service ->repository -> modal . but i do not what are we passing in the construct methods in each class.
in the below codes the flow goes like this: paycontroller -> Merchant service ->MerchantRepository->modal
the first one is paycontroller:
class PayController extends Controller
{
private $merchantService;
private $paymentService;
private $pay_request_field = array(
'orderID', 'hashKey','currencyCode','amount'
);
/**
* Create a new controller instance.
*
* #return void
*/
// public function __construct()
// {
// $this->middleware('auth');
// }
public function __construct(MerchantService $merchantService, PaymentService $paymentService){
$this->merchantService = $merchantService;
$this->paymentService = $paymentService;
}
is the constructor receiving a variable of the MerchantService and Payment service? if so where is the value coming from? im confused here
next is the MerchantService:
class MerchantService
{
private $merchantRepository;
private $merchantConfigRepository;
private $merchantPaymentRepository;
private $merchant;
private $merchantConfig;
private $merchantPayment;
public function __construct(MerchantRepository $merchantRepository, MerchantConfigRepository $merchantConfigRepository, MerchantPaymentRepository $merchantPaymentRepository){
$this->merchantRepository = $merchantRepository;
$this->merchantConfigRepository = $merchantConfigRepository;
$this->merchantPaymentRepository = $merchantPaymentRepository;
}
public function getMerchantById($id){
$this->merchant = $this->merchantRepository->getMerchantById($id);
$this->merchantConfig = $this->merchantConfigRepository->getMerchantConfig($this->merchant->mid);
return $this->merchant->toArray();
then the MerchantRepository:
class MerchantRepository
{
private $merchant;
public function __construct(Merchant $merchant){
$this->merchant = $merchant;
}
public function getMerchantByHash($hashKey="",$status='action'){
return $this->merchant->where([["hashKey","=",trim($hashKey)],["status","=",$status]])->firstOrFail();
}
public function getMerchantById($mid="",$status='action'){
return $this->merchant->where([["mid","=",trim($mid)],["status","=",$status]])->firstOrFail();
}
}
Then finally the Modal:
class Merchant extends Model
{
protected $connection = 'mysql1';
//Table Name
protected $table = 'merchants';
//Primary Key
protected $primaryKey = 'mid';
}
so whats my overall question is, what is going on in this whole process, and the constructors( parameters) where are they coming from.
Thanks in advance

In this example you are initializing the User and Database using the new keyword
Like:
$database = new Database('host', 'user', 'pass', 'dbname');
$user = new User($database);
And using the new keyword you need to manually resolve the dependency of the class you are initializing.
I have just pasted a line from here:
http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.new
An object will always be created unless the object has a constructor
defined that throws an exception on error. Classes should be defined
before instantiation (and in some cases this is a requirement)
Here for this block of code:
public function __construct(MerchantService $merchantService, PaymentService $paymentService){
$this->merchantService = $merchantService;
$this->paymentService = $paymentService;
}
You are adding it to the constructor of your Controller and you are lucky that all the dirty works are done for you by Laravel. i.e. dependency of the Controller will be automatically resolved for you by Laravel.
Laravel 5 has great dependency injection and you don't need to worry about from where the dependency is resolved.
Just have a look at this dependency Injection
http://slashnode.com/dependency-injection/
I hope you understood the differences.

class User
{
private $database = null;
public function __construct(Database $database) {
$this->database = $database;
}
public function getUsers() {
return $this->database->getAll('users');
}
}
$database = new Database('host', 'user', 'pass', 'dbname');
$user = new User($database);
$user->getUsers();
so in the above example, we are actually initiating a database object and passing it to the class's constructor. so my question is wwhere is this value of $merchant service being initiated and coming from ?
public function __construct(MerchantService $merchantService, PaymentService $paymentService){
$this->merchantService = $merchantService;
$this->paymentService = $paymentService;
}
in the above example of class, we can actually see that the $database is actually initiated outside the class...so i just dont understand where is the $merchantservice being initiated...

Related

How do I fetch the PHP DI container?

How do I load a database container using PHP DI?
This is one of the variations I have tried up until now.
Settings.php
<?php
use MyApp\Core\Database;
use MyApp\Models\SystemUser;
return [
'Database' => new Database(),
'SystemUser' => new SystemUser()
];
init.php
$containerBuilder = new \DI\ContainerBuilder();
$containerBuilder->addDefinitions('Settings.php');
$container = $containerBuilder->build();
SystemUserDetails.php
<?php
namespace MyApp\Models\SystemUser;
use MyApp\Core\Database;
use MyApp\Core\Config;
use MyApp\Helpers\Session;
/**
*
* System User Details Class
*
*/
class SystemUserDetails
{
/*=================================
= Variables =
=================================*/
private $db;
/*===============================
= Methods =
================================*/
/**
*
* Construct
*
*/
public function __construct(Database $db)
{
# Get database instance
// $this->db = Database::getInstance();
$this->db = $db;
}
/**
Too few arguments to function MyApp\Models\SystemUser\SystemUserDetails::__construct(), 0 passed in /www/myapp/models/SystemUser.php on line 54 and exactly 1 expected
File: /www/myapp/models/SystemUser/SystemUserDetails.php
Shouldn't the database get loaded automatically?
Trace:
Currrently, My main index.php file extends init.php which is the file where it create the container (pasted code part in the post).
Then I call the App class, which fetches the URL(mysite.com/login/user_login) and instantiate a new controller class and run the mentioned method, in this case, it's the first page - MyApp/Contollers/Login.php.
The user_login method fetches the credentials, validate them, and if they are valid, uses the SystemUser object to login.
SystemUser class:
namespace MyApp\Models;
class SystemUser
{
public $id;
# #obj SystemUser profile information (fullname, email, last_login, profile picture, etc')
protected $systemUserDetatils;
public function __construct($systemUserId = NULL)
{
# Create systemUserDedatils obj
$this->systemUserDetatils = new \MyApp\Models\SystemUser\SystemUserDetails();
# If system_user passed
if ( $systemUserId ) {
# Set system user ID
$this->id = $systemUserId;
# Get SysUser data
$this->systemUserDetatils->get($this);
} else {
# Check for sysUser id in the session:
$systemUserId = $this->systemUserDetatils->getUserFromSession();
# Get user data from session
if ( $systemUserId ) {
# Set system user ID
$this->id = $systemUserId;
# Get SysUser data
$this->systemUserDetatils->get($this);
}
}
}
}
PHP-DI is working correctly.
In your SystemUser class you are doing:
$this->systemUserDetatils = new \MyApp\Models\SystemUser\SystemUserDetails();
The constructor for SystemUserDetails requires a Database object, which you are not passing.
By calling new directly, you are not using PHP-DI. By doing this you are hiding the dependency, which is exactly what you are supposedly trying to avoid if you want to use a dependency injection system.
If SystemUser depends ("needs") SystemUserDetails, the dependency should be explicit (e.g. declared in its constructor).
Furthermore: You do not need a definitions file for a system like this. And the definitions file you show in your question doesn't follow the best practices recommended by PHP-DI.
Your design is far from perfect, and I'm not sure of your end-goals, but if you did something like this, it could work:
<?php
// src/Database.php
class Database {
public function getDb() : string {
return 'veryDb';
}
}
<?php
// src/SystemUserDetails.php
class SystemUserDetails {
protected $db;
public function __construct(Database $db)
{
$this->db = $db;
}
public function getDetails() {
return "Got details using " . $this->db->getDb() . '.';
}
}
<?php
// src/SystemUser.php
class SystemUser {
protected $details;
public function __construct(SystemUserDetails $details, $userId=null) {
$this->details = $details;
}
public function getUser() {
return "Found User. " .$this->details->getDetails();
}
}
<?php
//init.php
require_once('vendor/autoload.php');
// build the container. notice I do not use a definition file.
$containerBuilder = new \DI\ContainerBuilder();
$container = $containerBuilder->build();
// get SystemUser instance from the container.
$userInstance = $container->get('SystemUser');
echo $userInstance->getUser(), "\n";
Which results in:
Found User. Got details using veryDb.

PHP dependency injection in extended class

I'm just getting to grips with OOP, so sorry if this question seems a little all over the place, its how my head is feeling right now.
I have looked at constructors in the PHP docs but it does not seem to cover dependency injection.
I have a class called DatabaseLayer, this class simply creates a connection to my database
//php class for connecting to database
/**
* Class DatabaseLayer - connects to database via PDO
*/
class DatabaseLayer
{
public $dbh; // handle of the db connexion
/**
* #param $config Config- configuration class
*/
private function __construct(Config $config)
{
$dsn = $config->read('db.dsn');
$user = $config->read('db.user');
$password = $config->read('db.password');
$this->dbh = new \PDO($dsn, $user, $password);
}
public function getConnection()
{
if ($this->dbh)
{
return $this->dbh;
}
return false;
}
}
Q1: i have private function __construct(Config $config)
I'm not sure i fully understand the reason of having __construct(Config $config) instead of just using __construct($config)
Does (Config $config) automatically create $config as a new Config instance?
or do i have to do the following:
$config = new Config();
$dbLayer = new DatabaseLayer($config);
I want to extend the DatabaseLayer class and include methods for interacting with the database relating to my GameUser which is another class i have created, when i extend the DatabaseLayer class i need to inject the GameUser class
I know my new class DatabaseLayerUser inherits the methods and properties from the DatabaseLayer class.
Q2: as the new DatabaseLayerUser class is based on the 'DatabaseLayer' class it needs to have the Config class, because i used __construct(Config $config) does this get it automatically?
or do i have to pass both the Config and GameUser to DatabaseLayerUser
class DatabaseLayerUser EXTENDS DatabaseLayer
{
private $config; /** #var Config */
private $user; /** #var GameUser */
/**
* #param Config $config
* #param GameUser $user
*/
private function __construct(Config $config, GameUser $user){
$this->config = $config;
$this->user = $user;
}
/**
* profileExists - Checks to see if a user profile exists
* internal #var $PDOQuery
* #var $PDOStatement PDOStatement
* #throws Exception - details of PDOException
* #return bool
*/
private function profileExists()
{
try{
$PDOQuery = ('SELECT count(1) FROM userprofile_upl WHERE uid_upl = :uid');
$PDOStatement = $this->dbh->prepare($PDOQuery);
$PDOStatement->bindParam(':uid', $this->_userData->uid);
$PDOStatement->execute();
return $PDOStatement->fetchColumn() >0;
}catch(PDOException $e){
throw new Exception('Failed to check if profile exists for '.$this->_userData->uid, 0, $e);
}
}
}`
Q3: i saw there is a parent::__construct();
does this mean i should use:
private function __construct(Config $config, GameUser $user){
parent::__construct($config);
$this->user = $user;
}
I think you're really mixing up means and purposes here. The goal is never to use dependency injection per se, but to solve programming problems that you encounter with it. So let's first try and correct the dependencies between the classes and their responsibilities a little.
In my opinion, it is best to start this excercise from the application domain, start small, and then refactor if you want to introduce abstraction. At least when learning or as a thought experiment. When more experienced and when implementing the real thing, it is probably smarter to look a couple of steps ahead.
So for now I'll simply assume you're making an online game, with different users that are represented by GameUser objects, and no further than that.
The sole responsibility of the GameUser class should be to represent the domain data and logic, i.e.: it contains a couple of properties (let's say username and score) and methods (let's say incrementScore) that have to do with the application domain itself (game users). It should not be responsible for practical implementation details, most notably: how it gets persisted (written to file/db/whatever). That's why a DatabaseLayerUser that is responsible for storing itself is probably a bad idea. So let's start instead with a nice and clean GameUser domain class and use encapsulation (private properties to prevent tampering from the outside):
class GameUser {
private $_username;
private $_score;
public function __construct($username, $score) {
$this->_username = $username;
$this->_score = $score;
}
public function getUsername() { return $this->_username; }
public function getScore() { return $this->_score; }
public function incrementScore() {
$this->_score++;
return $this->_score;
}
}
We can now create a new GameUser ($user = new GameUser('Dizzy', 100);), but we cannot persist it. So we need some kind of repository where we can store these users and fetch them again later. Let's call it just that: the GameUserRepository. This is a service class. Later when there is more than one type of domain object and repository, we can create a DatabaseLayer class that will group these and/or act as a facade, but we start small now and will refactor later.
Once again, the responsibility of the GameUserRepository class is to allow us to fetch and store GameUser objects, and to check if a profile for a given username exists. In principle, the repository could store the GameUser objects in a file or somewhere else, but for now, we're making making the choice here to persist them in a SQL database (start small, refactor later, you get it.)
The responsibility of the GameUserRepository is not management of the database connection. It will however need a database object so it can pass the SQL queries to it. So it delegates the responsibility of setting up the connection and actually executing the SQL queries it will create.
Delegation rings a bell. Dependency injection comes into play here: we will inject a PDO database object (service). We'll inject it in the constructor (i.e. constructor DI as opposed to setter DI). It is then the responsibility of the caller to figure out how to create the PDO service, our repository really couldn't care less.
class GameUserRepository {
private $_db;
public function __construct(PDO $db) {
$this->_db = $db;
}
public function profileExists($username) {
try {
$PDOQuery = ('SELECT count(1) FROM userprofile_upl WHERE uid_upl = :uid');
$PDOStatement = $this->dbh->prepare($PDOQuery);
$PDOStatement->bindParam(':uid', $username);
$PDOStatement->execute();
return $PDOStatement->fetchColumn() >0;
} catch(PDOException $e) {
throw new Exception('Failed to check if profile exists for '. $username, 0, $e);
}
}
public function fetchGameUser($username) { ... }
public function storeGameUser(GameUser $user) { ... }
}
To answer Q1 & Q2 in one go: function __construct(PDO $db) simply expresses a type constraint: PHP will check that the $db parameter value is a PDO object. If you're trying to run $r = new GameUserRepository("not a PDO object");, it will throw an error. The PDO type constraint has nothing to do with dependency injection.
I think you're confusing this with the functionality of DI frameworks that actually inspect the constructor signature at run-time (using reflection), see that a PDO-type argument is needed, and then indeed create such an object automagically and pass it to the constructor when creating the repository. E.g. the Symfony2 DI bundle can do this, but it has nothing to do with PHP itself.
Now we can run code like this:
$pdo = new PDO($connectionString, $user, $password);
$repository = new GameUserRepository($pdo);
$user = $repository->fetchGameUser('Dizzy');
But this begs the question: what is the best way to create all these objects (services), and where do we keep them? Surely not by just putting the above code somewhere and using global variables. These are two clear responsibilities that need to be located somewhere, so the answer is that we create two new classes: the GameContainer class and the GameFactory class to create this container.
The responsibility of the GameContainer class is to centralize the GameUserRepository service with other services that we'll create in the future. The responsibility of the GameFactory class is to set up the GameContainer object. We'll also create a GameConfig class to configure our GameFactory:
class GameContainer {
private $_gur;
public function __construct(GameUserRepository $gur) { $this->_gur = $gur; }
public function getUserRepository() { return $this->_gur; }
}
class GameConfig { ... }
class GameFactory {
private $_config;
public function __construct(GameConfig $cfg) {
$this->_config = $cfg;
}
public function buildGameContainer() {
$cfg = $this->_config;
$pdo = new PDO($cfg->read('db.dsn'), $cfg->read('db.user'), $cfg->read('db.pw'));
$repository = new GameUserRepository($pdo);
return new GameContainer($repository);
}
}
We can now imagine a game.php application with basically the following code:
$factory = new GameFactory(new GameConfig(__DIR__ . '/config.php'));
$game = $factory->buildGameContainer();
One crucial thing is still missing however: the use of interfaces. What if we want to write a GameUserRepository that uses an external web service to store and fetch GameUser objects? What if we want to provide a MockGameUserRepository with fixtures to facilitate testing? We can't: our GameContainer constructor explicitly demands a GameUserRepository object as we have implemented it using the PDO service.
So, now is the time to refactor and extract an interface from our GameUserRepository. All consumers of the current GameUserRepository will now have to use the IGameUserRepository interface instead. This is possible thanks to dependency injection: references to GameUserRepository are all just used as type constraints that we can replace by the interface. It would not be possible if we hadn't delegated the task of creating these services to the GameFactory (which will now have the responsibility to determine an implementation for each service interface.)
We now get something like this:
interface IGameUserRepository {
public function profileExists($username);
public function fetchGameUser($username);
public function storeGameUser(GameUser $user);
}
class GameContainer {
private $_gur;
// Container only references the interface:
public function __construct( IGameUserRepository $gur ) { $this->_gur = $gur; }
public function getUserRepository() { return $this->_gur; }
}
class PdoGameUserRepository implements IGameUserRepository {
private $_db;
public function __construct(PDO $db) {
$this->_db = $db;
}
public function profileExists($username) {...}
public function fetchGameUser($username) { ... }
public function storeGameUser(GameUser $user) { ... }
}
class MockGameUserRepository implements IGameUserRepository {
public function profileExists($username) {
return $username == 'Dizzy';
}
public function fetchGameUser($username) {
if ($this->profileExists($username)) {
return new GameUser('Dizzy', 10);
} else {
throw new Exception("User $username does not exist.");
}
}
public function storeGameUser(GameUser $user) { ... }
}
class GameFactory {
public function buildGameContainer(GameConfig $cfg) {
$pdo = new PDO($cfg->read('db.dsn'), $cfg->read('db.user'), $cfg->read('db.pw'));
// Factory determines which implementation to use:
$repository = new PdoGameUserRepository($pdo);
return new GameContainer($repository);
}
}
So this really puts all pieces together. We can now write a TestGameFactory injecting the MockGameUserRepository, or, better still, extend GameConfig with a "env.test" boolean and have our existing GameFactory class decide whether to construct a PdoGameUserRepository or MockGameUserRepository based on that.
The connection with DI practices should now be clear as well. The GameContainer, of course, is your DI container. The GameFactory, is the DI container factory. It is these two that are implemented with all bells and whistles by DI frameworks such as the Symfony2 DI bundle.
You can indeed imagine extending the factory and its config up to the point where all services are completely defined in an XML file, including their implementation class names:
<container env="production">
<service name="IGameUserRepository" implementation="PdoGameUserRepository">
<connectionString>...</connectionString>
<username>...</username>
<password>...</password>
</service>
</container>
<container env="test">
<service name="IGameUserRepository" implementation="MockGameUserRepository"/>
</container>
You can also imagine generalizing the GameContainer so that services are fetched like $container->getService('GameUserRepository').
As for passing constructor arguments to parent classes in PHP (which has little to do with DI, except that it'll be needed sooner or later if you use constructor injection), you can do this as you suggest yourself:
class A {
private $_someService;
public function __construct(SomeService $service) { $this->_someService = $service; }
}
class B extends class A {
private $_someOtherService;
public function __construct(SomeService $service, SomeOtherService $otherService) {
parent::__construct($service);
$this->_someOtherService = $otherService;
}
}
$b = new B(new SomeService(), new SomeOtherService());
But you'll have to stay away from the private constructors. They reek of singletons.

php oop constructor

OK. here is what I'm trying to do:
class Image{
public $_image;
public $_extension;
public $_mime;
public $_size;
public $_location;
public $_description;
public function __construct($image, $location){
$this->_image = $image;
$this->_location = $location;
$this->_extension = getExtension();
$this->_mime = getMime();
$this->_size = getSize();
}
private functions fallow.....
}
But I keep getting an internal server error when I try to run it. When I comment out the method calls it works. So the question is can I call methods from inside the constructor or am I doing something wrong with the methods.
Do your functions getExtension, getMime and getSize exist? Are they methods on this class? If they are methods, they need to be called with $this->... as in
$this->_extension = $this->getExtension();
If they are not methods, and are functions, you need to make sure the files that contain/define them are loaded before you run the constructor.
Well ..this fragment of code will work as expected:
class Foo
{
protected $secret = null;
public function __construct( $data )
{
$this->secret = $this->makeSecret($data);
}
public function makeSecret( $data )
{
return md5( $data );
}
}
$bar = new Foo( 'lorem ipsum' );
That is not a problem.
But you should know, that is considered to be a bad practice - to do computation/work in the constructor. It makes that class practically untestable. Instead, if you need to perform some computation before "releasing" the object to the rest of the code, you should use a factory. Something along the lines of :
class ImageFactory
{
public function build($image, $location)
{
$instance = new Image($image, $location);
$instance->prepare();
return $instance;
}
}
The class would need some changes:
class Image
{
protected $_image; // you were leaking abstraction
protected $_extension;
protected $_mime;
protected $_size;
protected $_location;
protected $_description;
public function __construct($image, $location)
{
$this->_image = $image;
$this->_location = $location;
}
public function prepare()
{
$this->_extension = $this->getExtension();
$this->_mime = $this->getMime();
$this->_size = $this->getSize();
}
private functions fallow.....
}
Now when you need to create new object you do:
$factory = new ImageFactory;
$image = $factory->build( $file, '/uploads/' );
Of course the instance of ImageFactory can be reusable, and if all your images use the same $location, then you would pass that variable to factory at the initialization. And the factory would be able to "remember it" and pass to all the images it creates:
$factory = new ImageFactory('/uploads/');
$img1 = $factory->build( $file );
$img2 = $factory->build( $something_else );
This is actually how one should deal with creating multiple objects, which all need access to same DB connection instance.
Yes, you can call methods from within the constructor. Remember that the __construct() magic method was implemented in PHP 5. Prior to that, you created a function named the same as your class which acted as your constructor so depending on your PHP version, that could be a problem.
Additionally, the function calls you are making, are they in the class or external? If they are inside the class you need to call them this way:
$this->_extension = $this->getExtension();
You didnt specified what error you are expiriencing clearly. But try calling you class methods even inside the class using this keyword, otherwise it would not work:
public function __construct($image, $location)
{
$this->_image = $image;
$this->_location = $location;
$this->_extension = $this->getExtension();
$this->_mime = $this->getMime();
$this->_size = $this->getSize();
}
Would be a better idea to post your code for the methods you wrote. There could be something wrong within them as well. Possibly forgetting to return a result or something...

Organization of work with DataBase

I have some expierence in PHP, but have no one in application architecture
Now I want to orginize my own "bicycle". It's something not useful, maybe mini-framework or mini-application, I want get some exp here.
I need now to write classes for work with database and classese for entities (one of them isUser)
I have following code for database (some cheks and method are omitted to minify this question):
namespace DataBase;
class DataBase {
/**
*
* #var \PDO $pdo
*/
public $pdo;
public function __construct($host, $dbname, $username, $password=''){
$this->pdo = new \PDO('mysql:host='.$host.';dbname='.$dbname, $username, $password,
array(\PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'"));
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
}
/**
*
* #param string $statement
* #return Statement
*/
public function prepare($statement){
return new Statement($this->pdo->prepare($statement));
}
}
namespace DataBase;
class Statement {
private $stmt;
public function __construct(\PDOStatement $stmt) {
$this->stmt = $stmt;
}
public function query() {
try {
$this->stmt->execute();
return $this; //for chaining
}
public function bind($key, $value) {
$this->stmt->bindValue($key, $value, $this->typeof($value));
return $this; //for chaining
}
//some methods for fetching data(works with fetch,fetchAll, fetchColumn and different PDO::FETCH_ methods
public function fetchUpdate($obj) {
$this->stmt->setFetchMode(\PDO::FETCH_INTO, $obj);
$this->stmt->fetch();
}
public function fetchRow() {
return $this->stmt->fetch(\PDO::FETCH_OBJ);
}
public function fetchRowClass($class) {
return $this->stmt->fetchObject($class);
}
}
And Some dummy for User class
<?php
/**
* Description of User
*
* #author riad
*/
class User {
private $id;
private $name = null;
private $status = null;
private $hasInfo = false;
private static $cache=array();
public function __construct() {
}
public function getId() {
return $this->id;
}
public function getName() {
if(!$this->hasInfo)
$this->getInfo ();
return $this->name;
}
public function isAuthorized(){
return $this->status!="noauth";
}
public static function createById($id) {
// I want this method to cerate user from id, then get info only I will use it after that
if(array_key_exists($id,self::$cache)){
return self::$cache[$id];
}
$user = new User;
$user->id = $id;
return $user;
}
private function getInfo(){
try{
\FrontController::getInstance()->getDB()->
prepare('SELECT * FROM `users` WHERE `id`=:id')->
bind('id', $this->id)->query()->fetchUpdate($this);
$this->hasInfo = true;
}
catch(\DataBase\NotFoundException $dbe){
$this->status = "deleted";
}
}
public static function createFromRequest($request){
$user = new User;
try{
//try get data from cookie
\FrontController::getInstance()->getDB()->
prepare('SELECT * FROM `users` WHERE `session` = :session AND `id`= :id')->
bind('id', $id)->bind('session',$session)->query()->
fetchUpdate($user);
}
catch(... $e){
$user->status = "noauth";
$user->id = 0;
// make it unregged
}
return $user;
}
}
?>
I have some problems with it.
I don't want set properties from database, that are not listed in props of class list(is not so important, of course). I know that I can use public function __call($name,$value){
//do nothing;
}
I want to mkae this props private, but want also use $stmt->fetchUpdate($obj) I know I can use public function __call($name,$value){
$this->$name=$value;
}, but it's as declare props public and it is on the road with first point
I can also use public function __call($name,$value){
if($name=='id'){
$this->id=$value;
}
else if($name=='status'){
$this->status=$value;
}
} But it's not comfortable to write it for every entity class and not save as from publicity of this methods
I want to set $this->hasInfo to true when I get this class from database. I know I can change my Database class to always set some variable to true when by default it's false. But it seems to be not elegant.
I want to update cache when I set id (It maybe used as previos point)
Is it possible to avoid fetchRowClass write direct to props and use setter as with fetchUpdate? Or maybe allow fetchUpdate direct access?
I know I write a lot of code self but I want your opinion:
What should I improve?
What are other/the best possible solution for problems from previos list?
Hope, It's not so hard to read and understand.
Glad to see any suggestions
With regards Alex
Few tips: [based on my own experience and frameworks i already used]
Basically what you should/want/might do is to create a SuperClass for all the clases in you model. This class will contain the reference to the Database Instance, and it will have all the common methods for your model, i.e. getById($id), getAll(),getPaginated(), etc.
The other goal of this SuperClass is to map the results from the database into Instances of your Model's Classes. So in the end, your user class would have only properties, accessors and methods that are specific to the class, like special queries or something like that.
Here's an example of what this could look like:
Class Model{
protected function getById($_id){
$_table = get_class($this);
$anonymous = $this->_getObjectById($_table,$_id); //this method will execute a query (unsing PDO) and return a StdClass object with the results
$this->mapTable($anonymous,$_table); //this method will take the StdClass instance and will transform it into a $_table Instance
}
private function mapTable($stdInstance,$model_name){
foreach($stdInstance as $key => $value){
try{
if(property_exists($this,$key)){
$this->$key = $value; //you could declare the model's properties as protected... or you could create accessors and call them here
}
} catch(Exception $ex) {
/* something went wrong o_O */
}
}
Class User extends Model{
protected $id;
protected $name;
.....
}
Class Controller{
public function index(){
$user = new User();
$user->getById($_GET['id']);
print_r($user);
//now you can pass the $user object to the View to display it
}
}
in a few words... the Model class is a very small ORM. You could try to create your own ORM, (like i did) but you'll face a lot of problems when trying to map the relations between objects: Nx1,1xN,NxN,1x1, inheritance, "deeper relations" and the n+1 problem. You'll also need to, somehow, define the model structure so your ORM could understand it, maybe using YAML/XML files or reading the structure directly from the table structure of your databe, or having a naming convention in your properties...
its a really interesting field :)
Hope this helps and Good Luck

Database and OOP Practices in PHP

Its difficult to explain this situation but please see the example.
I have coded a website where the page loads, I initialize a database class. I sent this class as a function parameter to any functions that needs to access database.
I know this is bad approach but currently I have no clue how to do this any other way. Can you please help me.
Example
class sms {
function log_sms($message, $db) {
$sql = "INSERT INTO `smslog` SET
`mesasge` = '$message'
";
$db->query($sql);
if ($db->result)
return true;
return false;
}
}
then on the main page..
$db = new db(username,pass,localhost,dbname);
$sms = new sms;
$sms->log_sms($message, $db);
Is there any better approach than this ?
there are number of options how to resolve dependencies problem (object A requires object B):
constructor injection
class Sms {
function __construct($db) ....
}
$sms = new Sms (new MyDbClass);
setter injection
class Sms {
protected $db;
}
$sms = new Sms;
$sms->db = new MyDbClass;
'registry' pattern
class Registry {
static function get_db() {
return new MyDbClass;
}}
class Sms {
function doSomething() {
$db = Registry::get_db();
$db->....
}}
'service locator' pattern
class Loader {
function get_db() {
return new MyDbClass;
}}
class Sms {
function __construct($loader) {
$this->loader = $loader;
function doSomething() {
$db = $this->loader->get_db();
$db->....
}}
$sms = new Sms(new Loader);
automated container-based dependency injection, see for example http://www.sitepoint.com/blogs/2009/05/11/bucket-is-a-minimal-dependency-injection-container-for-php
interface DataSource {...}
class MyDb implements DataSource {...}
class Sms {
function __construct(DataSource $ds) ....
$di = new Dependecy_Container;
$di->register('DataSource', 'MyDb');
$sms = $di->get('Sms');
to name a few ;)
also the Fowler's article i gave you before is really worth reading
For starters you can make a protected $db variable in each of your classes that need to utilize the database. You could then pass $db in to the class constructor. Here's the updated code:
$db = new db(username,pass,localhost,dbname);
$sms = new sms($db);
$sms->log_sms($message);
class sms {
protected $db;
public function __construct($db) {
$this->db = $db;
}
public function log_sms($message) {
$sql = "INSERT INTO `smslog` SET
`mesasge` = '$message'
";
$this->db->query($sql);
if ($this->db->result)
return true;
return false;
}
}
This example is in PHP 5.
Singleton is also good practice in application design. They are very useful to avoid repeated queries or calculations during one call.
Passing Database object to every method or constructor is not a very good idea, use Singleton instead.
extend your database, or insert static method inside your db class. (I would also call for config within db constructor method)
class database extends db
{
public static function instance()
{
static $object;
if(!isset($object))
{
global $config;
$object = new db($config['username'],$config['pass'],$config['localhost'],['dbname']);
}
return $object;
}
}
make your method static
class sms {
public static function log($message) {
$sql = "INSERT INTO `smslog` SET `mesasge` = '$message'";
database::instance()->query($sql);
return (bool) database::instance()->result;
}
}
Next time you need to log sms just do static call like this
sms::log($message);
You could either have a static class that has two lists, one of available connections, the other of handed out connections, and just have a connection pool.
Or, if you are using something that has a quick connection time, such as MySQL, then just create the connection in your database class, do all the queries needed for that functionality, then close it. This is my preferred approach.

Categories