I am reading a lot of articles of PHP MVC use lately and found out I was programming my CMS all wrong...
Apparently the model-layer of an MVC contains multiple class instances to pass on data.
What I can't seem to figure out is how to apply this technique to my case. I hope you can help me shed light to the case:
class site_model {
private $iSite_id;
private $sSite_name;
//+ 8 other variables
function __construct($iSite_id = null, $sSite_name = null) {
$this->iSite_id= $iSite_id;
$this->sSite_name = $sSite_name;
}
public function get_all_sites() {
$aList = [];
$sSQL = 'SELECT * FROM sites WHERE 1';
$mReturn = mysql_query($sSQL);
while($aRow = mysql_fetch_assoc($mResult)) {
$aList[] = new site_model($aRow['site_id'], $['site_name']);
}
return $aList;
}
//+ 20 other query functions
}
class site_controller {
private $oSiteModel;
public function return_all_sites() {
$oSiteModel = new site_model();
return $oSiteModel->get_all_sites();
}
}
What I am worried about is when I create an array of model objects that contain a lot of db-functions (20+), and I don't even need most of the functions, I am loading a lot of useless data into the memory? I have no idea how much extra memory that is, but if I can avoid it, I think I should?
A few questions:
Is it good practice to store all the query-functions into the model?
If I load multiple instances of the model object, does the memory load in all these db-functions per instance or do they share the same functions stored in memory?
[I ask this because it's important to me to keep performance up, and I don't want to consume unnecessary resources]
Is using a separate db_model class the right way to go? With this I mean a model (data) just for setting and getting data and applying validations, and use another model(db) to contain all db functions and let the controller call the right db functions to set the data-model?
Ia it good practice to use a db-API class to execute the functions with data retrieved from models instead of the (db)model for better encapsulation and code reuse?
Like:
interface DatabaseAdapter
{
public function connect();
public function disconnect();
public function prepare($sql, array $options = array());
public function execute(array $parameters = array());
public function fetch($fetchStyle = null,
$cursorOrientation = null, $cursorOffset = null);
public function fetchAll($fetchStyle = null, $column = 0);
public function select($table, array $bind,
$boolOperator = "AND");
public function insert($table, array $bind);
public function update($table, array $bind, $where = "");
public function delete($table, $where = "");
//.. extra functions here for custom queries like joins etc..
}
Thank you in advance for helping me become a better programmer :)
Related
Is using computed include / require a bad code smell and does it have a bad impact on the performance? And I guess that having the included file execute code is also a bad thing to do, but is it ok if that behavior is documented?
Background information / Reason for my question:
I need to call an API to get information about some services. I have about 50 services with each service needing to call the API for 0-6 times. So I'm looking for a way to configure
The parameters for the API call (argumenttype differs between calls, may be a string but it also may be an array)
Define which API to call
I thought of having a single file for each service containing the calls and return the information as a single array like this:
<?php
// Params for Call 1
$anArrayWithSomeParams = array('param1', 'param2', 'param3', 'param4');
// Params for Call 2
$aString = 'string1';
$anotherString = 'string2'
// Params for Call 3-6
...
$someInformation = $dmy->getSomeInformation($anArrayWithSomeParams);
$notNeededHere = NULL;
$moreInformation = $dmy->getMoreInformation($aString,$anotherString);
...
$allData = array( '0' => $someInformation,
'1' => $notNeededHere
'2' => $tablespace,
....
);
?>
I then could include that file and use the variable alldata to access the data and do something with it like this:
require_once('class.dummy.php');
$directories = array("dir1", "dir2", "dir3");
$dmy = new dummy();
foreach($directories as $path) {
$allData = NULL;
$executeDataCollection = $path.'myFile.php';
require($executeDataCollection);
print "<pre>";
print_r($allData);
print "</pre>";
}
While this might work, it does not seem like an elegant solution. I was wondering if somebody could give me a hint towards a more elegant/sophisticated way of handling this.
Thanks in advance!
Using require and any of similiar approach is bad practice.
You should think more in OOP way how to implement this. To achieve something like this I would suggest to use interface and abstract class. In your case you need to call some APIS with different parameters on demand you should use following patterns/principles:
Adapter
Factory
Gateway
S.O.L.I.D - some of the principles will help you to design better what you need
Interface will look like:
interface ApiGateway {
/**
* This will execute call with optional parameters
*
**/
public function call($parameters = null);
}
Abstract class
abstract class ApiGatewayAbstract implements ApiGateway
{
/** return Adapter for handle API call **/
abstract protected function getAdapter();
/** return list of arguments for call */
abstract protected function getArguments();
public function call($parameters = null)
{
$adapter = $this->getAdapter();
$arguments = $this->getArguments();
// this will be HTTPAdapter for executing API call you need with specific params and arguments
return $adapter->execute($arguments, $parameters);
}
}
Now you can start implementing specific ApiGateways:
class MyApiGateway extends ApiGatewayAbstract
{
protected $arguments = [];
protected $adapter;
public function __construct(HttpClientInterface $httpClient, array $arguments = [])
{
$this->arguments = $arguments;
$this->adapter = $httpClient;
}
protected function getArguments()
{
return $this->arguments;
}
protected function getAdapter()
{
return $this->adapter;
}
}
Final step would be Factory for your ApiGateways:
class ApiGatewayFactory
{
// dynamic way to get Specific api gateway by name, or you can implement for each api gateway specific method
public function getApiGateway($name, HttpClientInterface $adapter, array $arguments)
{
$className = 'Namespace\\'.$name;
if (!class_exist($className) {
throw new \Exception('Unsuported ApiGateway');
}
// here you can use reflection or simply do:
return new $className($adapter, $arguments);
}
}
By this approach you will achieve clean way of what you want and also follow some of the principles from S.O.L.I.D. So you can add more ApiGateways with specific use cases, or different adapters ( soap, http, socket ) etc.
Hope this helps, also this is just an example have a look at the patterns and how to implement them. But this example should help you understand the approach.
Noob question. I'm working on a project which involves interacting with some legacy software and the database is not compatible with regular Laravel Relationships.
If I'm defining things in a constructor like this:
public function __construct(array $attributes = array())
{
parent::__construct($attributes);
$this->vatpercentage = $this->customer()->vatpercentage;
$this->vatcode = $this->customer()->vatcode;
$this->partrevision = $this->part()->revision;
$this->unitprice = $this->part()->unitprice;
}
public function part(){
return Part::findOrFail($this->partnum);
}
public function customer(){
$traderid = Order::where('id', $this->orderid)->value('traderid');
return Customer::where('id', $traderid)->where('tradertype', 'C')->first();
}
I need to reference customer(), part() and other similar functions many times in the constructor. Is the DB getting queried every time I reference $this->customer(), etc or is the result cached the first time I reference it and then used for all the other times following? Basically am I doing a lot of unnecessary DB calls by coding in this way rather than setting $this->customer = $this->customer() and grabbing the values like $this->customer->example?
No database query or method call is going to be cached automatically in your application, nor should it. Laravel and PHP aren't going to know how you want to use queries or methods.
Everytime you call customer(), you're building up and executing a new query. You could easily cache the result in a property if that's what you want, but you'd have watch the value of the $orderid property:
protected $customerCache;
public function customer()
{
if ($customerCache) return $customerCache;
$traderid = Order::where('id', $this->orderid)->value('traderid');
return $customerCache = Customer::where('id', $traderid)->where('tradertype', 'C')->first();
}
You're also performing too much in your constructor. I would highly recommend not performing queries in any constructors, constructors should be used to pass dependencies. The way you have it designed would make it very hard to unit test.
In Laravel 4.* there was a remember() method handling the cache in queries. It was removed from 5.1 with a valid reason that it's not the responsibility of Eloquent neither the query Builder to handle cache. A very very simplified version of a decorator class that could handle the caching of your queries:
final class CacheableQueryDecorator
{
private $remember = 60; //time in minutes, switch to load from config or add a setter
private $query = null;
public function __construct(Builder $builder)
{
$this->query = $builder;
}
private function getCacheKey(string $prefix = ''):string
{
return md5($prefix . $this->query->toSql() . implode(',', $this->query->getBindings()));
}
public function __call($name, $arguments)
{
$cache = Cache::get($this->getCacheKey($name), null);
if ($cache) {
return $cache;
}
$res = call_user_func_array([$this->query, $name], $arguments);
Cache::put($this->getCacheKey($name), $res, $this->remember);
return $res;
}
}
Using it:
$results = (new CacheableQueryDecorator($query))->get()
I'm trying to write a class that relies on some legacy code to fetch data from database related to a particular table (productcode)
For not repeating myself, I implement a private property $sql that holds the "evolving" query and finally applying getResult() on it (as this snippet shows it)
My question: is there any design principle violation by implementing this pattern? (specially adopting the "shared" $sql property). If the answer is yes, why and what is the proper way to do it?
<?php
class ProductCodeDataSource
{
use dbmanager_aware_trait;
private $sql;
public function findByProductId($productId)
{
$this->sql = [];
$this->sql['fields'] = 'productcode_value';
$this->sql['tables'] = 'productcode';
$this->sql['where'][] = sprintf('productcode_product_id = %d', (int) $productId);
$this->sql['order'][] = 'productcode_default';
return $this;
}
public function findOneByProductId($productId)
{
$this->findByProductId($productId);
$this->sql['limit'] = 1;
return $this;
}
public function getResult()
{
// get_all_value is a legacy method that fetches data from mysql DB
// build_select_ext is also a legacy method that builds sql queries from
// $sql array structures
return$this->dbmanager->get_all_value($db->build_select_ext($this->sql));
}
}
I'm trying to decide the design of a system which is meant to allow for a high amount of extensiblity. From what I can tell, a pattern such as the abstract factory would not allow for overriding of the base methods, apart from duplicating code (as demonstrated below).
I've done some preliminary research into aspect oriented programming and it seems to be along the lines of what I'm looking for but I'm having a difficult time wrapping my head around the specifics.
abstract class Object {
protected $object_id;
protected $name;
function LoadObjectData()
{
$file_contents = readfile('object'.$object_id.'.data');
$data = array();
// parse file contents into $data array...
return $data;
}
function Create()
{
$data = $this->LoadObjectData();
$name = $data['name'];
return $data;
}
}
class User extends Object {
protected $email_address;
function Create()
{
$data = parent::Create();
$this->email_address = $data['email_address'];
return $data;
}
}
//----------Module 1-MySQL Lookup-------------
/*
* Redefine Object::LoadObjectData() as follows:
*/
function LoadObjectData()
{
$data = array();
$result = mysql_query("SELECT...");
// construct array from result set
return $data;
}
//----------Module 2-Cache Machine-------------
/*
* Redefine Object::LoadObjectData() as follows:
*/
function LoadObjectData()
{
if (exists_in_cache($object_id)) {
return get_cached_object($object_id);
}
$data = parent::LoadObjectData();
cache_object($object_id, $data);
return $data;
}
(This is sort of a poor example, but hopefully it helps to get my point across)
The intended system would have a very large proportion of methods available to be extended and I would like to minimize the extra effort and learning necessary for developers.
Is AOP exactly what I'm looking for, or is there a better way to deal with this?
Thanks!
So, you want to use a decorator pattern without defining the decorator itself.
If yes, then it's a monkeypatching and can be done with aspect-oriented tools. This can be solved easily with following extensions and frameworks:
PHP Runkit Extension
Go! Aspect-Oriented framework for PHP
PHP-AOP Extension.
You don't have to declare the base class as an abstract class. You can make it a regular class and have it load and instantiate other classes based on passed construct parameters. The constructor can return an instance of a class, not just the class the constructor is in. To avoid duplicating code, you can mix static with instantiated functions and variables. Just remember that a static function or variable is the same for ALL instances. Change a static variable in one and it is changed for all instances. A rather basic example of a plugin architecture.
class BaseObject {
protected static $cache = array();
public function __construct($load_plugin) {
require_once($load_plugin.'.class.php');
$object = new $load_plugin();
return $object;
}
public static function cacheData($cache_key, $data) {
self::$cache[$cache_key] = $data;
}
}
class Plugin extends BaseObject {
public function __construct() {
}
public function loadData() {
// Check the cache first
if ( !isset(self::$cache[$cache_key]) ) {
// Load the data into cache
$data = 'data to cache';
self::cacheData($cache_key, $data);
}
return self::$cache[$cache_key];
}
}
Im thinking of making a custom datatypes / prototypes for a project im working on but im wondering if its such a good idea?
For example
class String
{
var $Value;
private $escaped = false;
function __construct($String)
{
$this->Value = $String;
}
function escape()
{
if($escaped === false)
{
$this->Value = Registry::get('Database')->escape($this->Value);
}
return $this;
}
function trim()
{
$this->Value = trim($this->Value);
return $this;
}
function __toString()
{
return $this->__toString();
}
}
$myValue = new String('Hello World')->trim()->escape();
//$myValue is now prepared for DB insert
There will be prototypes for Array, Object, String, Resource etc..
with arrays there will implement Iterator and such
Some benefits i have in mind is specific data types to objects for example
interface Insert
{
public function Insert(String $Value); //Array / Object / Resource
}
The custom prototypes would be useful for all strings.
But do you think that the amount of resource usage will out way the benefits ?
updated for POC
$String = new String('ValueText');
sprintf('Test %s',$String); //Works
trim($String); //Works
base64_encode($String); //Works
Also for arrays the SPL Library would be perfect.
class Array implements ArrayAccess, Iterator, Countable
{
public function __construct(){}
public function offsetSet($offset,$value){}
public function offsetExists($offset){}
public function offsetUnset($offset){}
public function offsetGet($offset){}
public function rewind(){}
public function current(){}
public function key(){}
public function next(){}
public function valid(){}
public function count(){}
}
Another idea would be the extendible entities
class DatabaseVariable extends String
{
function __construct($string)
{
parent::__constrcut($string);
}
public function escape()
{
//Blah
}
}
Having a new entity extend a data-type will make it inherit available methods for that data-type.
As discussed about autoboxing, this is the exact system im looking for but as its not passed discussions yet, for my new project (Forum System) witch I started the other day, do you think that I should go ahead and use my idea?, the user will be able to do faster interactions with datatypes, and if there is a function that does not support an object being passed, we can also do
$RawResource = $Resourtce->Raw();
//...
$Resource->Set($RawResource);
In my opinion, the time you spend writing this code, fixing this code, and cursing the fact that you can't use hundreds of PHP functions with your classes will outweigh any advantage this code may have.
Also, the developer who inherits your project will hate you.
Sounds like way too much trouble for... seemingly no benefit.
If you're worried about forgetting to escape stuff, stop escaping things altogether and start using parametrized queries.