Is it possible to override values from Model->fetchAll() so it work globally. I have tried to override this in model, but does not work:
class Application_Model_DbTable_OdbcPush extends Zend_Db_Table_Abstract
{
public function __get(string $col)
{
$res = parent::__get($col);
if ($col == "lastrun") {
$res = ($res == "1912-12-12 00:00:00+07" ? NULL : $res);
}
return $res;
}
//...
}
In a controller:
$odbcModel = new Application_Model_DbTable_OdbcPush();
$rs = $odbcModel->fetchAll( $select );
I want to override value returned from fetchAll(), find() etc when col name is "lastrun";
The way you're going about this isn't going to work. __get is used to get data from protected or private properties and typically used in conjunction with getters.
For example, if you implemented __get() in your Application_Model_DbTable_OdbcPush class you could do something like:
$model = new Application_Model_DbTable_OdbcPush();
//echo out the _primary property (primary key of the table)
echo $model->primary;
and expect it to work. Because _primary exists as a property in Zend_Db_Table_Abstract.
To do what you want to do you'll need to do it after the result set has been returned (unless you want to rewrite the whole Zend Db component). Just run the result set through a foreach and change the value of lastrun to whatever you want.
I tried to find a place to override the Zend Db components to do what you want, but it would involve to many classes.
Remember that when using DbTable classes, they only interact with one table. You'll need to duplicate code for every table you want to effect or you'll need to extend a base class of some kind.
You always have the option to use straight Sql to frame whatever query you can come up with.
Good Luck!
Found the answer, for community i share here :D
http://framework.zend.com/manual/1.12/en/zend.db.table.row.html
So we have to overload Zend_Db_Table_Row and assign it to model/dbtable:
class Application_Model_DbTable_Row_OdbcPush extends Zend_Db_Table_Row_Abstract
{
// do some override here
}
class Application_Model_DbTable_OdbcPush extends Zend_Db_Table_Abstract
{
protected $_name = 'odbcpush';
protected $_primary = 'id';
private $_global = null;
protected $_rowClass = "Application_Model_DbTable_Row_OdbcPush";
// etc
}
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'm new to zend framework, in this simple function i want to get a single 'post' and then i want to find all the comments in the related table
public function getPost($idPost)
{
$db= Zend_Registry::get('db');
$select=$db->select()
->from($this->_name, '*')
->where("idPost= ".$db->quote($idPost, 'INTEGER'));
$stmt=$select->query();
$rowset=$stmt->fetchAll();
$post=$rowset->current();
//ora devo aggiungerci i commenti che questo post ha ricevuto
$comm=$post->findDependentRowset('commenti');
$ris=array($post, $comm);
return $ris;
}
in my index controller i i simply call this function, but i get this error:
Call to a member function current() on a non-object in C:\xampp\htdocs\...
where's the mistake?
I think you have a few misconceptions about how you're using Zend_Db.
1. You're not using the ORM, just the PDO wrapper
Which means, your queries won't return Zend rowsets and rows and therefore you can't use the methods of you can use on those.
2. The default fetch mode
The default fetch mode of the Zend_Db_Statement fetchAll() method is array, if you want it to return an object (stdClass), change the fetch mode before fetching the data:
$stmt->setFetchMode(Zend_Db::FETCH_OBJ);
3. Using fetchAll() when you actually want one row
If you just want one row, then don't fetch a whole table! With Zend_Db_Statement, use for example:
$row = $stmt->fetch();
or
$rowObj = $stmt->fetchObject();
... again, that's not a zend row object, just a stdClass instance, but you can do:
$rowObj->some_field;
on it.
On the other hand, if this is a method in your Post model, it should look something like:
public function getPost($idPost)
{
return $this->getRow($idPost);
}
This will return the post, then, if you've setup the table relationships correctly, you can also query for the dependent data or just get all comments with that id separately.
The problem is that unless you define a table class as was previously mentioned you can't uuse the dependent or parent rowsets.
To make your current function work would be best done with two functions, and keep it simple:
public function getPost($idPost)
{
$db= new Zend_Db_Table($this->_name);
$select=$db->select()
->where("idPost= ?", $idPost);
/*Fetch just the row you want, or use fetchAll() if you need to match return types*/
$row = $db->fetchRow($select);
return $row;
}
public function getComments($table='comments', $id) {
$db = new Zend_Db_table($table);
$select = $db->select()->where('post_id = ?', $id)->order('date ASC');
$rowset = $db->fetchAll($select);
return $rowset/* or you could return an array ->$rowset->toArray() */
}
Zend_Db_Table is going to attempt to use the current database adapter, so all you need to do is pass in the tablename.
One more note: you don't need to use any of the quote() function when using select() it's taken care of.
But it is really important, that if you are going to use Zend_Db, you need to learn about "Defining table classes". At least enough to use them in your own classes.
I hope this helps!
To get a rowset and dependent rowset you have to use Zend_Db_Table.
You only use the Zend_Db_Adapter with Zend_Db_Select.
Read from here.
So you have to define a class which extends from Zend_Db_Table_Abstract.
Example:
class Bugs extends Zend_Db_Table_Abstract
{
protected $_name = 'bugs';
protected $_primary = 'bug_id';
}
To get the Zend_Db_Table_Rowset object use:
$bugs = new Bugs();
$rowset = $bugs->fetchAll("bug_status = 'NEW'");
To find dependent rowsets you have to define the relation in your table class. Look here how to define relationships.
I'm having issues accessing the Jobinfo class from my Deliveries. The problem is I need to be able to get the value of getQty from my child class and I also need to be able to get the qty_ship method using a property from the parent. How can I do this? It doesn't seem to work and quite confused over this... I'd like to be able to use methods from Parent->Child and Child->Parent dynamically.
class jobInfo
{
public $JOB_ID;
private $deliveries; // DELIVERIES CLASS
function __construct($job_id)
{
$this->JOB_ID=$job_id;
$this->deliveries = new Deliveries();
}
public function getQty()
{
return $this->query_s('job_sheet','*', 'job_id',$this->JOB_ID, 1, 'qty');
//returns a quantity from query method
}
}
class Deliveries extends jobInfo
{
function __construct(){}
public function qty_ship()
{
$qty = 0;
$SQL = mysql_query("SELECT * FROM deliveries WHERE jID='".parent::JOB_ID."'") or die(mysql_error());
$rows = mysql_num_rows($SQL);
if($rows>0)
{
while($data = mysql_fetch_array($SQL))
{
$qty += $data['qty_shipped'];
}
}
return $qty;
}
public function getTotalBO()
{
$qty = parent::getQty();
$totalship = $this->qty_ship();
$x = $qty-$totalship;
return $x;
}
}
$A = new Jobinfo(15);
You want $this->getQty() and $this->JOB_ID, but for completeness, consider:
Removing your empty no-parm constructor, as it cannot actually be used to instantiate the class unless it calls the parent contructor with a job id, which it cannot do since you expect the job id to be defined externally.
Making JOB_ID protected. For better encapsulation you might instead make it private and provide a getJobId() method.
Working on consistent class naming - jobInfo starts with lowercase and Deliveries starts with uppercase.
Working on consistent function naming - You have underscore-seperated functions mixed with camel-case functions.
Working on consistent spacing - You mix 1-character, 2-character and 0-character spacings throughout without much rhyme or reason.
Welcome to OOP and Good Luck!
If you extend from that class, then you should be using
$this->getQty();
$this will return the current object wich already include the parent with all the public and protected variables and methods.
You should only use :: when you call a static class
for exemple :
jobInfo::getQty();
You might also want to take a look at the naming convention.
http://framework.zend.com/manual/1.12/en/coding-standard.naming-conventions.html
So I have a very simple (at least right now it is) model that returns a contact via it's primary key ID:
class Model_Contact extends \Fuel\Core\Model
{
public function get_by_id($contact_id)
{
return Entity_Contact::find_by_pk($contact_id);
}
}
The Entity_Contact class looks like this (irrelevant array content omitted):
class Entity_Contact extends \Core\Entity_Base
{
protected static $_table_name = 'contacts';
protected static $_properties = array(...);
protected static $_public_settable_properties = array(...);
protected static $_rules = array(...);
}
Note: \Core\Entity_Base extends \Fuel\Core\Model_Crud
I might use this in a controller like so:
$model = new Model_Contact();
$contact = $model->get_by_id(4);
I know that in order to unit test this, I should mock out the actual database call (Entity_Contact::find_by_pk), but I'm not sure how to do this. Since I'm using Fuel's Model_crud functionality (where the DB accessors are actually a part of the domain object model), I'm not sure that I can completely mock the database---or maybe I'm missing something.
So the question: how would you write a test for Model_Contact::get_by_id()?
Thanks in advance!
You can mock it easily if you use AspectMock.
https://github.com/Codeception/AspectMock
Your test may have to create the objects so you can test your methods.
Create the objects that get stored to the database.
Test get_by_id
Delete your created object
It's not the most glorious thing in the world, but it would accomplish testing your method... meh?