I'm writing a system around an existing database structure using Laravel 4.1. The current system is based around two websites which use their own table, a and b, both of which are identical. This is an unavoidable problem until we rewrite the other system.
I need to be able to query both tables at the same time using Eloquents Query Builder, so I may need to get a list of rows from both tables, or INSERT or UPDATE from either at any time.
Currently we have a model for both tables, but no way to link between them and implement the missing methods such as all or find.
Our thought is to have an Interface which will bind these results together, however we're not sure how to go about this at all.
<?php
interface HotelInterface {
public function all();
public function find();
}
use Illuminate\Database\Model;
class Hotel implements HotelInterface {
}
?>
Is all we have so far.
I asked on the Laravel forums and got the answer I was looking for! I'm reposting here incase.
What we're actually after is a Repository which would look like this:
<?php
class HotelRepository {
public $A;
public $B;
public function __construct(A $A, B $B) {
$this->A = $A;
$this->B = $B;
}
public function find($iso = NULL, $hotelid = NULL) {
$A = $B = NULL;
if($iso !== NULL) {
$A = $this->A->where('country', $iso);
$B = $this->B->where('country', $iso);
if($hotelid !== NULL) {
$A = $A->where('id', $hotelid);
$B = $B->where('id', $hotelid);
}
}
if($hotelid !== NULL) {
if($A->first()) {
return $A->first();
}
if($B->first()) {
return $B->first();
}
}else{
return $A->get()->merge($B->get());
}
}
public function all() {
$aCollection = $this->A->all();
$bCollection = $this->B->all();
return $aCollection->merge($bCollection);
}
}
Now in the controller where I want to call this, I just add:
<?php
class HomeController extends BaseController {
public function __construct(HotelRepository $hotels) {
$this->hotels = $hotels;
}
}
And I can now use $this->hotels to access the find and all method that I created.
If the tables are identical you should only need one model, the connection is the only thing that needs to change. Have a read here: http://fideloper.com/laravel-multiple-database-connections.
There's a Eloquent method on() for specifying the connection, here's an example:
The Eloquent example looks like what you need:
$results = Model::on('mysql')->find(1);
Add both connections to your database config and then change the on() part depending on which DB you need to query.
Update: misunderstood the question
If you only need to change the table and not the database, you can use setTable()
$model = new Model
$model->setTable('b');
$model->find(1);
Although that may get confusing.
Instead, you could also define a base model and then extend it with the only difference being the table protected $table = 'b';
You can always change the default database for the one you need. I use this Config::set('database.default', 'chronos');, where chronos is one of my databases. When I need to change to the "other", I just change de database name. You can call it wherever you want. I think that what you're looking for is just switch between the databases.
You need to have two different models, one for each table on each database, though.
Let me know if I got it wrong.
Related
I have a question about extending my own Models eloquent.
In the project I am currently working on is table called modules and it contains list of project modules, number of elements of that module, add date etc.
For example:
id = 1; name = 'users'; count = 120; date_add = '2007-05-05';
and this entity called users corresponds to model User (Table - users) so that "count" it's number of Users
and to update count we use script running every day (I know that it's not good way but... u know).
In that script is loop and inside that loop a lot of if statement (1 per module) and inside the if a single query with count. According to example it's similar to:
foreach($modules as $module) {
if($module['name'] == 'users') {
$count = old_and_bad_method_to_count('users', "state = 'on'");
}
}
function old_and_bad_method_to_count($table, $sql_cond) {}
So its look terrible.
I need to refactor that code a little bit, because it's use a dangerous function instead of Query/Builder or Eloquent/Model and looks bad.
I came up with an idea that I will use a Models and create Interface ElementsCountable and all models that do not have an interface will use the Model::all()->count(), and those with an interface will use the interface method:
foreach ($modules as $module) {
$className = $module->getModelName();
if($className) {
$modelInterfaces = class_implements($className);
if(isset($modelInterfaces[ElementsCountable::class])) {
/** #var ElementsCountable $className */
$count = $className::countModuleElements();
} else {
/** #var Model $className */
$count = $className::all()->count();
}
}
}
in method getModelName() i use a const map array (table -> model) which I created, because a lot of models have custom table name.
But then I realize that will be a good way, but there is a few records in Modules that use the same table, for example users_off which use the same table as users, but use other condition - state = 'off'
So it complicated things a little bit, and there is a right question: There is a good way to extends User and add scope with condition on boot?
class UserOff extends User
{
protected static function boot()
{
parent::boot();
static::addGlobalScope(function (Builder $builder) {
$builder->where('state', '=', 'off');
});
}
}
Because I have some concerns if this is a good solution. Because all method of that class NEED always that scope and how to prevent from method withoutGlobalScope() and what about other complications?
I think it's a good solution to create the UserOff model with the additional global scope for this purpose.
I also think the solution I would want to implement would allow me to do something like
$count = $modules->sum(function ($module) {
$className = $module->getModelName();
return $className::modulesCount();
}
I would create an interface ModulesCountable that mandates a modulesCount() method on each of the models. The modulesCount() method would return either the default count or whatever current implementation you have in countModuleElements().
If there are a lot of models I would probably use a trait DefaultModulesCount for the default count, and maybe the custom version too eg. ElementsModuleCount if that is consistent.
All my classes that connect to a database need to get values of custom columns from their respective tables. So instead of coding a function for each class, is there a way for me to implement a base class from which my classes extend and I can use that base class function to easily get and update data on my database (at least for simple data).
class Users extend BaseClass
{
private $table = "users";
private $columns = ["name", "email", "password"];
}
so from an outside function, I can access the email value like this
Users->where("name", "John")->getEmail();
or possibly
Users->where("name", "John")->get("email");
I could also use this method to update data to the database. The functions where should be universal so it should exist in BaseClass. (I know the database queries that I should use, what I want to know is how to call get after calling where and also possibly setting multiple where requirements).
Users->where("name", "John")->where("last_name", "Smith")->get("email");
I think you want something like this
abstract class BaseClass
{
private $where_clauses=[];
private $columns=[];
private $table='';
protected function setData($table,$cols){
$this->columns=$cols;
$this->table=$table;
}
public function where($key, $value){
$this->where_clauses[$key]=$value;
return $this;
}
public function get($col){
$sql='SELECT '.$col.' FROM '.$this->table.' WHERE';
$first=true;
foreach($this->where_clauses AS $key=>$val){
if(!$first) sql.=' AND ';
$first=false;
$sql.=$key.' = '.$val;
}
// RUN QUERY, Return result
}
}
Note that the where function returns a reference to $this, which is what let's you string the function calls together (not tested the code). This would also need some adapting to let you put two conditions on the same column.
I use Redbeanphp ( http://redbeanphp.com/ ) in my php project. And i want to use a table prefix for my tables.
Redbeanphp can't support table prefix since the version 3.0. But i want to extend Redbeanphp to support table prefix in my project.
I don't want to modify the redbeanphp code. But if there's no solution, i'll do that.
I have already tried to replace the QueryWriter of Redbeanphp but the QueryWriter class is not always the same (it depends of the type of my database).
What is the best way to do that ?
I now got the response so i answer to myself.
Once redbean is initialized, you can configure a new toolbox. The toolbox in redbean handle 3 important objects : The query writer, the Redbean OODB and the database adapter. You can access the current redbean toolbox with R::$toolbox
You can do this code :
R::configureFacadeWithToolbox(new RedBean_ToolBox(R::$redbean, R::$adapter, R::$writer));
This code does nothing. Because you configure Redbean with a new toolbox but with the same OODB, the same database adapter and the same query writer. But in this code, you can replace one of these object by your own object.
Example, replacing the writer by a dummy writer :
$writer = new MyQueryWriter();
R::configureFacadeWithToolbox(new RedBean_ToolBox(R::$redbean, R::$adapter, $writer));
The probem is the following :
You want to replace the query writer by your own query writer to handle a table prefix
The query writer class is not always the same. Redbean use 5 classes for the query writer. The class depends of the database type. For instance, if you use a Mysql database, the query writer class is RedBean_QueryWriter_MySQL
You don't want to write an entire query writer.
Redbean query writer possible classes are :
RedBean_QueryWriter_CUBRID
RedBean_QueryWriter_MySQL
RedBean_QueryWriter_Oracle
RedBean_QueryWriter_PostgreSQL
RedBean_QueryWriter_SQLiteT
So, this is my solution. I wrote 5 littles classes.
class MyCubridQueryWriter extends RedBean_QueryWriter_CUBRID {
public function safeTable($name, $noQuotes = false) {
$name = prefix($name);
return parent::safeTable($name, $noQuotes);
}
}
class MyMysqlQueryWriter extends RedBean_QueryWriter_MySQL {
public function safeTable($name, $noQuotes = false) {
$name = prefix($name)
return parent::safeTable($name, $noQuotes);
}
}
class MyOracleQueryWriter extends RedBean_QueryWriter_Oracle {
public function safeTable($name, $noQuotes = false) {
$name = prefix($name)
return parent::safeTable($name, $noQuotes);
}
}
class MyPostgreSqlQueryWriter extends RedBean_QueryWriter_PostgreSQL {
public function safeTable($name, $noQuotes = false) {
$name = prefix($name)
return parent::safeTable($name, $noQuotes);
}
}
class MySQLiteTQueryWriter extends RedBean_QueryWriter_SQLiteT {
public function safeTable($name, $noQuotes = false) {
$name = prefix($name)
return parent::safeTable($name, $noQuotes);
}
}
As you can see, each class extend a Redbean query writer class. We override the safeTable method. Redbean always use safeTable on a table name. The prefix function is simple :
function prefix($table) {
return "my_prefix_$table";
}
So now, in our code. We can use an array to map a Redbean query writer class to our own classes and replace it. Here we are :
$writerMapping = array(
'RedBean_QueryWriter_CUBRID' => 'MyCubridQueryWriter',
'RedBean_QueryWriter_MySQL' => 'MyMysqlQueryWriter',
'RedBean_QueryWriter_Oracle' => 'MyOracleQueryWriter',
'RedBean_QueryWriter_PostgreSQL' => 'MyPostgreSqlQueryWriter',
'RedBean_QueryWriter_SQLiteT' => 'MySQLiteTQueryWriter'
);
$class = $writerMapping[get_class(R::$writer)];
$writer = new $class(R::$adapter);
R::configureFacadeWithToolbox(new RedBean_ToolBox(R::$redbean, R::$adapter, $writer));
Et voila. Now Redbean will use your own writer and you can do what you want ! With our safeTable method, we add a prefix to every table name in the database.
I ran into this problem when wanting to use RedBean with Wordpress. My solution was to create another class (WPR for "wordpress redbean"), like so:
class WPR {
public static function __callStatic($method, $params)
{
global $wpdb;
$prefix = $wpdb->base_prefix;
foreach ($params as &$param)
$param = preg_replace('/\{([a-zA-Z0-9_]+)\}/', $prefix . '$1', $param);
// switch to wordpress database
R::selectDatabase('WPR');
// do the dang thing
$res = call_user_func_array(array('R',$method),$params);
// go back
R::selectDatabase('default');
// send it
return $res;
}
};
R::addDatabase('WPR', "mysql:host=".DB_HOST.";dbname=".DB_NAME, DB_USER, DB_PASSWORD);
I also wanted this class to use a different database than my 'regular' redbean class, so I have the selectDatabase() calls in there. Comment them out if you don't need them.
What it does is it acts as a proxy to redbean, but with each input it checks for some substring like {this} and it expands it out into the full database name, with prefix. Here's an example of your usage:
$my_blog = WPR::find('{blogs}', 'domain=?', array('mydomain.com')); or
$allowed_hosts = WPR::getCol('SELECT domain FROM {blogs}');
In those two cases, {blogs} gets converted to wp_blogs
Magus,
I have the same problem as you. I tried your solution but could not get it working. I wrote a couple of functions for prefixing and my object names into table names and back, which I think will work in my case, but I'd still like to get your way working since it'll be more transparent. I have unprefixed table names working for reading and writing.
I noticed was that Oracle support isn't available out-of-the-box in RedBean, so I added checks for each classname to avoid errors:
if (class_exists('RedBean_QueryWriter_MySQL', false)) {
class MyMysqlQueryWriter extends RedBean_QueryWriter_MySQL {
...
}
The checks should work, I got output to my log within my MySQL (which I'm using) block while loading the prefixing code.
Also, at the end there you wrote:
$class = $writerMapping(get_class(R::$writer));
but you probably meant:
$class = $writerMapping[get_class(R::$writer)];
Based on some debugging, my R::$writer has been changed after configureFacadeWithToolbox, but, for some reason the table names aren't being converted, and nothing within my custom safeTable function is being executed.
If you could give any more info on how you tested your method or what I could be missing, I'd be glad to hear it.
(I'm sorry this message isn't an answer to your question, but I really couldn't find any other way to send you a message or comment on your answer. Damn Stack Overflow! (Just kidding, I love it.))
I've an ORM model (PHP Active Record), say, for a blogging system. I've something that's a post model that stores the number of likes. The post could either be a picture or quote (say), and they are different tables (and hence models).
The schema is that a post holds data like number of shares, likes, description, etc. along with either a picture or a quote.
So when writing getters for the post model I'm having to write
public function getX() {
if ($this->isPicture()) {
return $this->picture->getX();
}
else if ($this->isQuote()) {
return $this->quote->getX()
}
else {
return self::DEFAULT_X
}
}
I'm currently having to write this structure for many getter. Is there something I can do to avoid that?
PS: Tagged as PHP because that's my code in.
EDIT
Changed comments to code.
This is a model (and a corresponding table in the DB) that has more data than just a picture and quote. Example, description that's part of the post and doesn't reside on either the picture or the quote.
There's tables for pictures and quotes.
Using PHP Active Record and each of the three classes extends the generic model class provided by PHP Active Record.
The picture model has it's own data. Same for quote.
To expand on the idea of the Strategy pattern mentioned in the comments:
class Post {
// get the correct 'strategy'
public function getModel() {
if ($this->isPicture()) {
return $this->picture;
}
if ($this->isQuote()) {
return $this->quote;
}
return null;
}
// using the strategy
public function getX() {
$model = $this->getModel();
if (null === $model) {
return self::DEFAULT_X;
}
return $model->getX();
}
}
Each strategy would presumably implement the same interface as the Post class for exposing those getters. Even better would be to provide a default strategy (rather than returning null) and have that return the default values. That way, the null check in each getter becomes redundant.
An alternative approach to this is a very basic form of metaprogramming. The idea is that you go a level higher than calling your methods by hand, and let the code do it for you.
(Assume that the method definitions are all part of Post)
public function getX($model = null) {
if ($model) return $model->getX();
else return self::DEFAULT_X;
}
// usage
$postModel->getX($pictureModel);
What's happening here is that, in this single instance of getX in your Post model, you're passing in the name of another class, and executing the `getX' method on that instance (if it exists and is callable).
You can extend this in other ways. For example, maybe you don't want to pass an instance in, when the method can do it anyway:
public function getX($model_name = null) {
if ($model_name && $class_exists($model_name) && is_callable(array($model_name, 'getX')) {
$model = new $model_name;
return $model->getX();
} else {
return self::DEFAULT_X;
}
}
// usage
$postModel->getX('Picture');
In this instance, you pass the model in as a string, and the method will do the rest. While this makes it quicker to get what you want, you might find that you don't want to work with fresh instances all the time (or you can't), so there's a bit of a trade-off with this 'convenient' way.
That still doesn't fully solve your problem, though, since you still have to repeat that for each getter, over and over again. Instead, you can try something like this:
public function __call($method, $args) {
$class = $args[0];
if (class_exists($class) && is_callable(array($class, $method))) {
$model = new $class;
return $model->$method();
}
}
// usage
$postModel->getX('Picture');
$postModel->getY('Quote');
$postModel->getZ('Picture');
If you call a function that doesn't exist on the Post model, that magic method will be called, and it'll fire up a new instance of the model name you supply as an argument, and call the getWhatever method on it, if it exists.
It's important to note that you must not define these getters in Post, unless you want to override the methods in the other classes.
There is still the problem of this creating new instances all the time, though, and to remedy this you can use a bit of dependency injection. This means that you let the Post class contains a list of other instances of classes that it wants to use in future, so you can add and remove them at will.
This is what I would consider the actual solution, with the other examples hopefully showing how I've got here (will edit to clarify things, of course).
public $models = array();
public function addModel($instance) {
$this->models[get_class($instance)] = $instance;
}
public function __call($method, $args) {
$class = $args[0];
if (array_key_exists($class, $this->models)) {
$model = $this->models[$class];
if (is_callable(array($model, $method)) {
return $model->$method();
}
}
}
// usage
$this->addModel($pictureModel);
$this->addModel($quoteModel);
$this->getX('Picture');
$this->getY('Quote');
Here, you're passing in your existing instances of models into the Post class, which then stores them in an array, keyed by the name of the class. Then, when you use the class as described in the last example, instead of creating a new instance, it will use the instance it has already stored. The benefit of this is that you might do things to your instances that you'd want reflected in the Post model.
This means that you can add as many new models as you like that need to plug into Post, and the only thing you need to do is inject them with addModel, and implement the getters on those models.
They all require you to tell the class what models to call at some point or another. Since you have an array of dependent models, why not add a way to get everything?
public function __call($method, $args) {
$class = $args[0];
if (array_key_exists($class, $this->models)) {
$model = $this->models[$class];
if (is_callable(array($model, $method)) {
return $model->$method();
}
} elseif ($class === 'all') {
// return an array containing the results of each method call on each model
return array_map(function($model) use ($method) {
if (is_callable(array($model, $method) return $model->$method();
}, $this->models);
}
}
// usage
$postModel->getX('all');
Using this, you'll get an array containing the return values of each getX method on each model you added with addModel. You can create pretty powerful functions and classes that do all this stuff without you having to repeat tedious logic.
I have to mention that these examples are untested, but at the very least I hope the concept of what you can do has been made clear.
Note:
The same thing can be applied to __GET and __SET methods, too, which are used for accessing properties. It's also worth saying that there may be the slight risk of a library already using these magic methods, in which case you'll need to make the code a little more intelligent.
So basically I'm making a leap from procedural coding to OOP.
I'm trying to implement the principles of OOP but I have a nagging feeling I'm actually just writing procedural style with Objects.
So say I have a list of pipes/chairs/printers/whatever, they are all all listed as products in my single table database. I need to build a webapp that displays the whole list and items depending on their type, emphasis is on 'correct' use of OOP and its paradigm.
Is there anything wrong about just doing it like:
CLass Show
{
public function showALL(){
$prep = "SELECT * FROM myProducts";
$q = $this->db-> prepare($prep);
$q->execute();
while ($row = $q->fetch())
{
echo "bla bla bla some arranged display".$row['something']
}
}
and then simply
$sth = new show();
$sth->showAll();
I would also implement more specific display methods like:
showSpecificProduct($id)->($id would be passed trough $_GET when user say clicks on one of the links and we would have seperate product.php file that would basically just contain
include('show.class.php');
$sth = new show();
$sth->showSpecificProduct($id);
showSpecificProduct() would be doing both select query and outputing html for display.
So to cut it short, am I going about it allright or I'm just doing procedural coding with classes and objects. Also any ideas/hints etc. on resolving it if I'm doing it wrong?
As well as the model practices described by #Phil and #Drew, I would urge you to separate your business, data and view layers.
I've included a very simple version which will need to be expanded upon in your implementation, but the idea is to keep your Db selects separate from your output and almost "joining" the two together in the controller.
class ProductController
{
public $view;
public function __construct() {
$this->view = new View;
}
public function indexAction() {
$model = new DbProductRepository;
$products = $model->fetchAll();
$this->view->products = $products;
$this->view->render('index', 'product');
}
}
class View
{
protected $_variables = array();
public function __get($name) {
return isset($this->_variables['get']) ? $this->_variables['get'] : null;
}
public function __set($name, $value) {
$this->_variables[$name] = $value;
}
public function render($action, $controller) {
require_once '/path/to/views/' . $controller . '/' . $action . '.php';
}
}
// in /path/to/views/product/index.php
foreach ($this->products as $product) {
echo "Product ID {$product['id']} - {$product['name']} - {$product['cost']}<br />\n";
}
A better fit would be to implement a repository pattern. An example interface might be
interface ProductRepository
{
public function find($id);
public function fetchAll();
}
You would then create a concrete implementation of this interface
class DbProductRepository implements ProductRepsoitory
{
private $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
public function find($id)
{
// prepare execute SQL statement
// Fetch result
// return result
}
public function fetchAll()
{
// etc
}
}
It's generally a bad idea to echo directly from a method or function. Have your methods return the appropriate objects / arrays / whatever and consume those results.
The scenario you are describing above seems like a good candidate for MVC.
In your case, I would create a class strictly for accessing the data (doing selects of product categories or specific products) and then have a different file (your view) take the output and display it.
It could look something like this:
class Product_Model {
public function find($prodId) { ... }
public function fetchAll($category = '') { ... }
public function search($string) { ... }
}
Then somewhere else you can do:
$products = new Product_Model();
$list = $products->fetchAll(37); // get all from category 37
// in true MVC, you would have a view that you would assign the list to
// $view->list = $list;
foreach($ilst as $product) {
echo "Product ID {$product['id']} - {$product['name']} - {$product['cost']}<br />\n";
}
The basic principle of MVC is that you have model classes that are simply objects representing data from some data source (e.g. database). You might have a mapper that maps data from the database to and from your data objects. The controller would then fetch the data from your model classes, and send the information to the view, where the actual presentation is handled. Having view logic (html/javascript) in controllers is not desirable, and interacting directly with your data from the controller is the same.
first, you will want to look into class autoloading. This way you do not have to include each class you use, you just use it and the autoloader will find the right file to include for you.
http://php.net/manual/en/language.oop5.autoload.php
each class should have a single responsibility. you wouldn't have a single class that connects to the database, and changes some user data. instead you would have a database class that you would pass into the user class, and the user class would use the database class to access the database. each function should also have a single responsibility. you should never have an urge to put an "and" in a function name.
You wouldn't want one object to be aware of the properties of another object. this would cause making changes in one class to force you to make changes in another and it eventually gets difficult to make changes. properties should be for internal use by the object.
before you start writing a class, you should first think about how you would want to be able to use it (see test driven development). How would you want the code to look while using it?
$user = new User($db_object);
$user->load($id);
$user->setName($new_name);
$user->save();
Now that you know how you want to be able to use it, it's much easier to code it the right way.
research agile principles when you get a chance.
One rule of thumb is that class names should usually be nouns, because OOP is about having software objects that correspond to real conceptual objects. Class member functions are usually the verbs, that is, the actions you can do with an object.
In your example, show is a strange class name. A more typical way to do it would be to have a class called something like ProductViewer with a member function called show() or list(). Also, you could use subclasses as a way to get specialized capabilities such as custom views for particular product types.