I have several classes that are basically interfaces to database rows. Since the class assumes that a row already exists ( __construct expects a field value ), there is a public static function that allows creation of the row and returns an instance of the class.
Here's a pseudo-code ( so there are mistakes and missing improvements in this ) example :
class fruit {
public $id;
public function __construct( $id ) {
if ( ! is_numeric($id) ) {
throw new Exception("Id is not numeric.");
}
$this->id = $id;
$sql = "SELECT * FROM Fruits WHERE id = $id";
...
$this->arrFieldValues[$field] = $row[$value];
}
public function __get( $var ) {
return $this->arrFieldValues[$var];
}
public function __set( $var, $val ) {
$sql = "UPDATE fruits SET $var = " . mysql_real_escape_string($val) . " WHERE id = $this->id";
}
public static function create( $fruit ) {
$sql = "INSERT INTO Fruits ( fruit_name ) VALUE ( '" mysql_real_escape_string($fruit) . "' )";
$id = mysql_insert_id();
$fruit = & new fruit($id);
return $fruit;
}
}
$obj1 = fruit::create( "apple" );
$obj2 = & new fruit( 12 );
What is this pattern called?
Edit: I changed the example to one that has more database-interface functionality. For most of the time, this kind of class would be instantiated normally, through __construct(). But sometimes when you need to create a new row first, you would call create().
I think it's the Factory method pattern.
The factory method pattern is an object-oriented design pattern to implement the concept of factories.
Like other creational patterns, it deals with the problem of creating objects (products) without specifying the exact class of object that will be created. The factory method design pattern handles this problem by defining a separate method for creating the objects, which subclasses can then override to specify the derived type of product that will be created.
Outside the scope of design patterns, the term factory method can also refer to a method of a factory whose main purpose is creation of objects.
As this is related to databases, I think this is close to something that may be called Data Mapper.
If you are looking for something like this in PHP, move to Propel ORM. Look at the first example : it's almost your code !
Related
I have the following example in which I tend to use a couple of classes, to create a simple web app.
The file hierarchy seems like this.
> cupid
- libs
- request
- router
- database
- view
- bootstrap.php
- index.php
The index.php just calls the bootstrap.php which in turn contains something like this:
// bootstrap.php
namespace cupid
use request, router, database, view;
spl_autoload_register(function($class){ /* autoload */ });
$request = new view;
$response = new response;
$router = new router;
$database = new database;
$router->get('/blog/{id}', function($id) use ($database, $view) {
$article = $database->select("SELECT blog, content FROM foo WHERE id = ?",[$id]);
$view->layout('blogPage', ['article'=>$article]);
});
As you can probably tell, my problem is this line:
$article = $database->select("SELECT blog, content FROM foo WHERE id = ?", [$id]);
Which I don't want to use, and instead try a " Domain Object Model " approach.
Now, given that I will add another folder called domain, with blog.php
> cupid
- domain
- Blog.php
- libs
...
And fill blog.php with properties mapping table rows, and getter and setters ..
namespace App\Domain;
class Blog {
private $id, $title, $content, $author;
public function getTitle(){
return $this->title;
}
public function setTitle($title){
$this->title = $title;
}
...
}
My question is: Assuming my understanding of DOM is so far correct, and that I have a CRUD/ORM class, or a PDO wrapper to query the database;
"How can I tie together, i.e. the blog model with the PDO wrapper to fetch a blog inside my bootstrap file?"..
As far as a Domain Object you basically already have written one, your blog object. To qualify as a domain model all a class must to is to provide a representation along with any of the functionality of a concept within your problem space.
The more interesting problem here and the one you appear to be struggling with is how to persist a domain model. Keeping with the tenet of the single responsibility principle your Blog class should deal with being a blog post and doing the things that a blog post can do, not storing one. For that you would introduce the concept of a repository of blog posts that would deal with storing and retrieving objects of this type. Below is a simple implementation of how this can be done.
class BlogRepository {
public function __construct(\cupid\database $db){
$this->db = $db;
}
public function findById($id){
$blogData = $this->db->select("select * from blog where id = ?", [$id]);
if ($blogData){
return $this->createBlogFromArray($blogData);
}
return null;
}
public function findAllByTag($tag){...}
public function save(Blog $blog) {...}
private function createBlogFromArray(array $array){
$blog = new Blog();
$blog->setId($blogData["id"]);
$blog->setTitle($blogData["title"]);
$blog->setContent($blogData["content"]);
$blog->setAuthor($blogData["author"]);
return $blog;
}
}
Then your controller should look something like this.
$router->get('/blog/{id}', function($id) use ($blogRepository, $view) {
$article = $blogRepository->findById($id);
if ($article) {
$view->layout('blogPage', ['article'=>$article]);
} else {
$view->setError("404");
}
});
To truly be SOLID the above class should be a database specific implementation of a BlogRepository interface to adhere to IoC. A factory should also probably be supplied to BlogRepository to actually create the blog objects from data retrieved from the store.
In my opinion one of the great benefits of doing this is you have a single place where you can implement and maintain all of your blog related interactions with the database.
Other Advantages to this method
Implementing caching for your domain objects would be trivial
Switching to a different data source (from flat files, blogger api, Document Database Server,PostgresSQL etc.) could be done easily.
You can alternatively use a type aware ORM for a more general solution to this same problem. Basically this Repository class is nothing more than a ORM for a single class.
The important thing here is that you are not talking directly to the database and leaving sql scattered throughout your code. This creates a maintenance nightmare and couples your code to the schema of your database.
Personally I always tend to stick the database operations in a database class which does all the heavy lifting of initialising the class, opening the connection etc. It also has generic query-wrappers to which I pass the SQL-statements which contains the normal placeholders for the bound variables, plus an array of the variables to be bound (or the variable number of parameters approach if thats suits you better). If you want to bind each param individually and not use the $stmt->execute(array()); You just pass in the types with the value in a data structure of your choosing, multi dim array, dictionary, JSON, whatever suits your needs and you find easy to work with.
The model class it self (Blog in your case) then subclasses the Database. Then you have a few choices to make. Do you want to use the constructor to create only new objects? Do you want it to only load based on IDs? Or a mix of both? Something like:
function __construct(id = null, title = null, ingress = null, body = null) {
if(id){
$row = $this->getRow("SELECT * FROM blog WHERE id = :id",id); // Get a single row from the result
$this->title = $row->title;
$this->ingress = $row->ingress;
$this->body = $row->body;
... etc
} else if(!empty(title,ingress,body)){
$this->title = title;
... etc
}
}
Maybe neither? You can skip the constructor and use the new(title, ingress, body), save() and a load(id) methods if thats your preference.
Of course, the query part can be generalised even further if you just configure some class members and let the Database-superclass do the query building based on what you send in or set as member-variables. For example:
class Database {
$columns = []; // Array for storing the column names, could also be a dictionary that also stores the values
$idcolumn = "id"; // Generic id column name typically used, can be overridden in subclass
...
// Function for loading the object in a generic way based on configured data
function load($id){
if(!$this->db) $this->connect(); // Make sure we are connected
$query = "SELECT "; // Init the query to base string
foreach($this->columns as $column){
if($query !== "SELECT ") $query .= ", "; // See if we need comma before column name
$query .= $column; // Add column name to query
}
$query .= " FROM " . $this->tablename . " WHERE " . $this->idcolumn . " = :" . $this->idcolumn . ";";
$arg = ["col"=>$this->idcolumn,"value"=>$id,"type"=>PDO::PARAM_INT];
$row = $this->getRow($query,[$arg]); // Do the query and get the row pass in the type of the variable along with the variable, in this case an integer based ID
foreach($row as $column => $value){
$this->$column = $value; // Assign the values from $row to $this
}
}
...
function getRow($query,$args){
$statement = $this->query($query,$args); // Use the main generic query to return the result as a PDOStatement
$result = $statement->fetch(); // Get the first row
return $result;
}
...
function query($query,$args){
...
$stmt = $this->db->prepare($query);
foreach($args as $arg){
$stmt->bindParam(":".$arg["col"],$arg["value"],$arg["type"]);
}
$stmt->execute();
return $stmt;
}
...
}
Now as you see the load($id), getrow($query,$args) and query($query,$args) is completely generic. ´getrow()´is just a wrapper on query() that gets the first row, you may want to have several different wrappers that to or interpret your statement result in different ways. You may also even want to add object specific wrappers to your models if they cannot be made generic. Now the model, in your case Blog could look like:
class Blog extends Database {
$title;
$ingress;
$body;
...
function __construct($id = null){
$this->columns = ["title","ingress","body","id",...];
$this->idcolumn = "articleid"; // override parent id name
...
if($id) $this->load($id);
}
...
}
Use it as so: $blog = new Blog(123); to load a specific blog, or $blog = new Blog(); $blog->title = "title"; ... $blog->save(); if you want a new.
"How can I tie together, i.e. the blog model with the PDO wrapper to fetch a blog inside my bootstrap file?"..
To tie the two together, you could use an object-relational mapper (ORM). ORM libraries are built just for glueing your PHP classes to database rows. There are a couple of ORM libraries for PHP around. Also, most ORMs have a built in database abstraction layer, which means that you can simply switch the database vendor without any hassle.
Considerations when using an ORM:
While introducing a ORM also introduces some bloat (and some learning), it may not be worthwhile investing the time for simply a single Blog object. Although, if your blog entries also have an author, one or multiple categories and/or associated files, an ORM may soon help you reading/writing the database. Judging from your posted code, an ORM will pay off when extending the application in the future.
Update: Example using Doctrine 2
You may have a look at the querying section of the official Doctrine documentation to see the different options you have for read access. Reconsider the example you gave:
// current implementation
$article = $database->select("SELECT blog, content FROM foo WHERE id = ?",[$id]);
// possible implementation using Doctrine
$article = $em->getRepository(Blog::class)->find($id);
However, ideally you define your own repository to separate your business logic from Doctrines API like the following example illustrates:
use Doctrine\ORM\EntityRepository;
interface BlogRepositoryInterface {
public function findById($id);
public function findByAuthor($author);
}
class BlogRepsitory implements BlogRepositoryInterface {
/** #var EntityRepository */
private $repo;
public function __construct(EntityRepository $repo) {
$this->repo = $repo;
}
public function findById($id) {
return $this->repo->find($id);
}
public function findByAuthor($author) {
return $this->repo->findBy(['author' => $author]);
}
}
I hope the example illustrates how easily you can separate your business domain models and logic from the underlying library and how powerful ORMs can come into play.
Codeigniter can return a database query as generic "Object" like:
$q = $this->db->get("some_table");
$obj = $this->q->row();
$var = $obj->some_property
In my case I want to make a PHP class who's public variables are 1 for 1 with the database columns, along with some public methods. Is there a quick one-shot way to cast or convert the generic "Row" object into my custom class object? I've read posts that hint that it is certainly possible, but most involve a really hacky serialize/deserialize solution. In the past I have just done:
public function __construct($row) {
$this->prop = $row->prop;
$this->id = $row->id;
$this->value = $row->value;
}
And I find this is very tedious and makes ugly code.
See the third section under result():
CodeIgniter User Guide: Generating Query Results
You can also pass a string to result() which represents a class to instantiate for each result object (note: this class must be loaded)
$query = $this->db->query("SELECT * FROM users;");
foreach ($query->result('User') as $row)
{
echo $row->name; // call attributes
echo $row->reverse_name(); // or methods defined on the 'User' class
}
I want to load 2 objects from my DB. The first object is the parent and second inherit of the first (in PHP and DB).
I've created 2 class :
(it's only a sample not the real code so don't try to correct this ;-) )
class A{
...
public static function get($id){
$query = "SELECT id,field1,field2 FROM table_A WHERE id = $id";
$result = request($query);
return load_object_A_instance($result);
}
...
}
class B extends A{
...
public static function get($id){
$query = "SELECT id,field3,field4 FROM table_B WHERE id = $id";
$result = request($query);
return load_object_B_instance($result);
}
...
}
I would instantiate object B with its own properties and with properties of object A in the "same" action. How can I do this ?
I've some ideas but I don't see how to implement them :
class B extends A{
...
public static function get($id){
$query = "SELECT id, field3, field4 FROM table_B WHERE id = $id";
$result = request($query);
$B = load_object_B_instance($result);
if($B != empty/null){
$B = merge(A::get($B->id),$B); // <== that's the part I don't know how to implement
}
return $B;
}
...
}
Edit :
I found a first solution (it's not clean but ...)
echo $obj->name; //show: carlos
echo $obj2->lastname; //show: montalvo here
$obj_merged = (object) array_merge((array) $obj, (array) $obj2);
$obj_merged->name; //show: carlos
$obj_merged->lastname; //show: montalvo here;
Solution found here: How do I merge two objects?
Just because you use classes, does not make it OOP. If you want to merge data from two data source, you retrieve information from them and then a 3rd partt to combined then:
$foo = new A( $connection );
$bar = new B( $connection );
$data = $foo->get( 42 ) + $bar->get( 1 );
Also you should look into DataMapper pattern, and watch some video on the subject of proper OOP:
Inheritance, Polymorphism, & Testing
Advanced OO Patterns (slides)
Unit Testing
Global State and Singletons
Don't Look For Things!
You can do that with single-table inheritance:http://www.jacopobeschi.com/post/php-laravel-single-table-inheritance
I've got a number of product classes which extend a parent product class. Each of these implements its own version of a PRODUCT_ID const. A method in a customer object will pull the PRODUCT_ID 's from the DB. I'd then like to instanciate the relevant product object and add it to an array.
Any ideas how I can dynamically find out which product object has the specific PRODUCT_ID const?
Sorry if that is a bit confusing!
The only way to do this would be to first loop through all of the declared classes, then check if they're a subclass of the parent product and finally to check if the PRODUCT_ID constant is defined and equal to the product you're looking for.
Here an example:
<?php
class Product
{
}
class Stack extends Product
{
const PRODUCT_ID = 1;
}
class Overflow extends Product
{
const PRODUCT_ID = 2;
}
function getSubclasses($parentClassName)
{
$classes = array();
foreach (get_declared_classes() as $className)
{
if (is_subclass_of($className, $parentClassName))
$classes[] = $className;
}
return $classes;
}
function find_product($product_id)
{
foreach ( get_declared_classes() as $class )
if ( is_subclass_of($class, 'Product') )
if ( constant($class . '::PRODUCT_ID') == $product_id )
return $class;
}
echo find_product(1); // Outputs "Stack"
Create class with field $product_id and accessors:
class Product
{
protected $product_id;
public function __construct($id)
{
$this->id = $id;
}
public function getProductId()
{
return $this->product_id;
}
public function setProductId($product_id)
{
$this->product_id = $product_id;
}
}
And then:
$t = new Product(1254899);
Each PRODUCT_ID value corresponds to a unique product class name, right ? So you can't have a PRODUCT_ID without a class associated ? So, in this case, you know how many product class you have.
If everything is true, I think the best solution is to create an array like this
$pairs = array(
PRODUCT_ID_VALUE_ONE => 'classNameOne',
PRODUCT_ID_VALUE_TWO => 'classNameTwo',
// [...]
);
Then, when you want to retrieve the class name associated with a specific PRODUCT_ID, you have to do
$className = $pairs[ $myProductId ];
You have two solutions to construct that array:
build it manually,
build it using automatically PHP reflexion (as shown by Francois)
If you have few class and if they don't change a lot, the first solution is better: its faster, easy to read/change values, easy to understand the code, and you have a better control over the array values => you set them manually so you won't have wrong values.
The problem with the first solution is you may have inconsistencies between the array and all the products class: if you change a PRODUCT_ID value into a class, you'll have to change it manually into the array.
The second solution is better if you have a lot of classes or if they change often. However, PHP reflexion may slow down your application. To limit that side effect, you can:
build the array once and only once,
build the array once only when needed,
You can create a method like getProductClassById which will:
check the existence of the array above,
created the array if necessary,
search in that array,
return the value,
Hope this will help,
Cheers
I have an issue that is quite annoying with symfony 1.2 and propel.
I have a model where I have implemented inheritance using the single-table strategy. So, here is an excerpt of my model:
Ad (id, posted_date, description)
then RealEstateAd(location, price, transaction_type) and JobAd(position, requirements, company) which inherit both from Ad.
I would like to display all ads, but I would like to display a RealEstateAd differently from a JobAd. To achieve this, I've used a partial for a RealEstateAd and a partial for a JobAd.
So, in the action, I did this:
$c = new Criteria();
$this->allAds = AdPeer::doSelect($c);
In the template, I check the class of each object:
$add = $allAds[$i];
if ($add instanceof RealEstateAdd)
//Use the RealEstatePartial
The problem is that class of an object in the $allAds array is sfOutputEscaperObjectDecorator.
So, nothing is displayed at all.
How could I deal with this issue? is there a way to get an array with objects which are actually of the class RealEstateAd or JobAd? How is the hydrating process carried out here?
sfOutputEscaperObjectDecorator has a raw method to get the undelying object.
Anyway, the best thing you can do is to have three different classes (i assume that real estates and job ads are Models)
class Ad { public function __toString() { print 'ad'; } }
class RealEstates extends Ad { public function __toString() { print 'realad'; } }
class JobAd extends Ad { public function __toString() { print 'jobad'; } }
so you can just call print $myAd; in your view without checking the object types.
(use polymorphism luke)
I don't know much about symfony or propel, so if i'm way off base here i apologize and just ignore this post...
What if you create a helper function getAdType() that uses some methodology to distinguish between the different types of ads.
function getAdType( $ad ) {
if ( isset( $ad->position ) ) {
return 'job';
}
elseif ( isset( $ad->transaction_type ) ) {
return 'realestate';
}
}
$add = $allAds[$i];
if ( getAdType( $add ) == 'realestate' )
//Use the RealEstatePartial
I might be misunderstanding something, but unless you have overloaded AdPeer::doSelect(), then it will only return an array of instance of Ad.
If you were to post your schema, it would be easier for me or others to help as it is not really clear how you've built your object model. Is RealEstateAd a propel class defined in schema.yml? or is it a custom class you've added to lib?
Eitherway, AdPeer::doSelect* will only return Ad, so it sounds like what you need is a custom retriever in the AdPeer. Again, more info about your schema will help.