I overloaded in one of my models the afterFind() function:
public function afterFind()
{
parent::afterFind();
echo "<PRE>";
echo var_dump($this);
echo "</PRE>";
die();
}
I get the dump in case that the query result is NOT empty.
But this is not getting called in case that the query that gets executed returns no result.
I need to call it especially in the case that no result is found to try to load the requested data via a different source then.
How can i achieve this?
[EDIT]
Find gets called by:
return $this->hasOne(Myclass::className(), ['id' => 'key_id']);
since hasOne(...) uses
public function hasOne($class, $link)
{
/* #var $class ActiveRecordInterface */
/* #var $query ActiveQuery */
$query = $class::find();
$query->primaryModel = $this;
$query->link = $link;
$query->multiple = false;
return $query;
}
::find() method is from ActiveRecord class and creates your ActiveQuery object
->afterFind() method is from ActiveQuery class/object, but it gets triggered only in case query returns non-empty result
If you need to do some action regardless of whether query returned result or not, you can:
Simply use your relation method
$query = $this->hasOne(Myclass::className(), ['id' => 'key_id']);
// Do your stuff here and...
return $query;
If you search for some more global solutions, then you can extend yii\db\ActiveRecord inside your own active record class, e.g. app\components\MyActiveRecord and override __get method. Then use MyActiveRecord as a base class for your models (Myclass in your example) instead of usual ActiveRecord.
namespace app\components;
class MyActiveRecord extends \yii\db\ActiveRecord {
public function __get($name)
{
if (isset($this->_attributes[$name]) || array_key_exists($name, $this->_attributes)) {
return $this->_attributes[$name];
} elseif ($this->hasAttribute($name)) {
return null;
} else {
if (isset($this->_related[$name]) || array_key_exists($name, $this->_related)) {
return $this->_related[$name];
}
$value = parent::__get($name);
if ($value instanceof ActiveQueryInterface) {
$result = $this->_related[$name] = $value->findFor($name, $this);
// Do your stuff here.
return $result;
} else {
return $value;
}
}
} }
Related
I am having trouble getting value of the class member setting in another method within the class. I have tried use __get and __set magic methods, getter and setter as well as two more approach as in the code but none of them working.
What I am looking is if the type is U than the javascript variable should be used else not.
Approach one
class UserRatings extends User {
private $postType; // string
public function headJS(){
// access postType value from getItem method
// this outputs nothing (blank)
if ($this->postType = 'U') {
# code...
}
}
public function getItem($post){
$this->postType = $post['data']['post_type'];
$markup = 'html markup to render the output';
return $this->postType;
}
public function isType($post)
{
if ($post == 'U') {
$this->isType = true;
}
return $this->isType;
}
}
Approach two
class UserRatings extends User {
private $isType = false;
public function headJS(){
// even this doesnt't work too
if ($this->isType) {
# code...
}
}
public function getItem($post){
$markup = 'html markup to render the output';
$type = $post['data']['post_type'];
$this->isType($type);
}
public function isType($post)
{
if ($post == 'U') {
$this->isType = true;
}
return $this->isType;
}
}
You first approach will not work as $isType will always be false. Because it’s not initialized and even when you initialize it with your function isType($post) you give it trueas a value. However you check in your headJS() if $this->isType ==‘U’ so always false.
For the second approach everything seems fine. My only guess is that you are calling HeadJS() before isType($post) or the value of $post is always different than ‘U’
you have missed $ sign at $this->isType(type);.
You have to just call $this->headJS(); after $this->isType = true;
class UserRatings extends User {
private $isType = false;
public function headJS(){
// even this doesnt't work too
if ($this->isType) {
# code...
}
}
public function getItem($post){
$markup = 'html markup to render the output';
$type = $post['data']['post_type'];
$this->isType($type);
}
public function isType($post)
{
if ($post == 'U') {
$this->isType = true;
$this->headJS();
}
return $this->isType;
}
}
so I have this JSON object, which I have converted into a PHP object, for example I can use $apiobject->Response->DataItems and get a response.
This is stored in a class called returnjsonObject with a public function called getJsonObject.
In the same file how can I populate another class using the data from this $apiobject into something like:
class Response
{
public $StatusCode;
}
Then how can I for example echo out $StatusCode
Here is part of my file:
class Response
{
public $StatusCode; //*I want this to equal $apiobject->Response->DataItems*
}
class returnjsonObject{
public function getJsonObject()
{
echo"<pre>";
$apiobject = json_decode($response);
var_dump($apiobject->Response->DataItems);
echo"<pre>";
}
I've heard of using $this but I have to admit I don't understand it.
tl;dr I need to use $apiobject to populate $StatusCode using $apiobject->Response->DataItems
Hope you understand my question :-)
You can use setter and getter
class Response{
public $StatusCode;
/**
......
*/
public function __construct($attributes = Array()){
// Apply provided attribute values
foreach($attributes as $field=>$value){
$this->$field = $value;
}
}
function __set($name,$value){
if(method_exists($this, $name)){
$this->$name($value);
}
else{
// Getter/Setter not defined so set as property of object
$this->$name = $value;
}
}
function __get($name){
if(method_exists($this, $name)){
return $this->$name();
}
elseif(property_exists($this,$name)){
return $this->$name;
}
return null;
}
}
$DataItems =$apiobject->DataItems;
$response = new Response($DataItems);
echo $response->StatusCode;
Code not tested though. Learn more here.
http://www.beaconfire-red.com/epic-stuff/better-getters-and-setters-php
I would first set all attributes in Response Class from $apiobject like this
<?php
class Response {
public $StatusCode;
/* more attributes
...
*/
public $Name
} //end of Response class
Then instantiate Response class and set attributes
$response = new Response();
$response->StatusCode =$apiobject->DataItems->StatusCode;
$response->Name=$apiobject->DataItems->Name;
/* Set all other properties
.......
*/
echo $respose->StatusCode; //output status code
Note: you use $this to make reference to a function or a property in the current class. i.e
Class Example {
public $name;
public function getThatName(){
return $this->name; //we make reference to property `$name`
}
}
$this can only be use in a Class or Object to reference a property or a function in itself.
You can not do this.
$example = new Example();
$this->name; //Wrong because you are not in `Example` class
I have extended PHP's mysqli class, which works fine. But how can I make it return a custom result object (or a boolean for insert/update/delete etc) when querying?
namespace MyApp;
class MySQLi extends \mysqli {
public function query($query, $resultmode = null) {
// This needs to return a MySQLiResult or a boolean
}
}
class MySQLiResult extends \mysqli_result {
}
Doing this I can return a MySQLiResult object, but I can't figure out how to return a boolean for non select based queries:
public function query($query, $resultmode = null) {
$this->real_query($query);
return new MySQLiResult($this);
}
Update:
This is what I ended up using:
class MySQLi extends \mysqli {
public function query($query, $resultmode = null) {
$result = parent::query($query, $resultmode);
return is_bool($result) ? $result : new MySQLiResult($result);
}
}
class MySQLiResult {
private $result;
public function __construct(mysqli_result $result) {
$this->result = $result;
}
public function __call($name, $arguments) {
return call_user_func_array(array($this->result, $name), $arguments);
}
public function __set($name, $value) {
$this->result->$name = $value;
}
public function __get($name) {
return $this->result->$name;
}
}
Phil's answer is OK, but it is possible to just extend MySQLi_Result by checking mysqli::field_count.
Checkout the documentation for mysqli::field_count
This function can be useful when using the mysqli_store_result() function to determine if the query should have produced a non-empty result set or not without knowing the nature of the query.
This is just what we needed.
public MySQL extends MySQLi
{
public function query($query)
{
if ($this->real_query($query)) {
if ($this->field_count > 0) {
return new MySQL_Result($this);
}
return true;
}
throw new MySQL_Exception($this->error, $this->errno);
}
}
Now you can extend your result class from MySQLi_Result and implement some useful interfaces like SeekableIterator so you can use foreach on your resultset:
class MySQL_Result extends MySQLi_Result implements Countable, SeekableIterator, ArrayAccess
{
...
}
Probably the simplest thing to do would be treat your MySQLiResult class as a decorator for mysqli_result. For example
class MySQLiResult
{
private $result;
public function __construct(\mysqli_result $result)
{
$this->result = $result;
}
}
You could then proxy method calls to the internal result and decorate (add functionality) where required.
I have a problem to get an object from an array-collection of objects by ID.
The following is my code:
protected $_rootLocation;
public function __construct(Location $rootLocation)
{
$this->_rootLocation= $rootLocation;
var_dump($this->_rootLocation);
}
public function getLocationById($id)
{
$value = null;
foreach($this->_rootLocationas $root)
{
if ($id == $root->getId())
{
$value = $root;
break;
}
}
return $value;
}
Then the function return "NULL" so it's dosn't work...
Edit
I do like that :
$manager = new LocationManager($rootLocation);
echo "<pre>";
var_dump($manager->getLocationById('291'));
echo "</pre>";
Your function returns null because the object is not found!
It depends on the implementation of the myClasse object, this must implement the iterator interface and the getId() method must return a valid Id on each iteration.
Imagine that none of all objects in the array has the ID you're looking for. Your function will just return null. For example with an empty array.
As you can see, returning null does not mean that the function does not work. It works perfectly and did what you specified, it is just, that no such object exists.
It's then up to you to decide what to do if this happens. As you've not told in your question, there is not much to add but to offer you some options:
You could check if the function returns null and then take it as a "not found" case.
$result = $collection->getObjectById($id);
if (null === $result) {
# object not found
} else {
# object found
}
You can throw an Exception inside the function if the function should only be called for existing objects:
public function getObjectById($id) {
foreach ($this->_rootObject as $root) {
if ($id == $root->getId()) {
return $root;
}
}
throw new InvalidArgumentException(sprintf('Not a valid ID: %d', $id));
}
or finally:
Offer an additional function to check for an existing ID first:
private function findById($id) {
foreach ($this->_rootObject as $object) {
if ($id == $object->getId()) {
return $object;
}
}
return null;
}
public function hasObjectById($id) {
return null !== $this->findById($id);
}
public function getObjectById($id) {
if (null !== $root = $this->findById($id)) {
return $root;
}
throw new InvalidArgumentException(sprintf('Not a valid ID: %d', $id));
}
Also you might be interested to create yourself a class called that encapsulates your needs, so you do not need to implement that in your "I manage the root collection object " object which is more than indirect. This then is basically your own collection class. An example:
interface Identifiable {
public function getId();
}
/**
* Example Object Class
*/
class MyObject implements Identifiable {
private $id;
public function __construct($id) {
$this->id = (int) $id;
}
public function getId() {
return $this->id;
}
}
/**
* Example Collection
*/
class IdentifiableCollection implements Countable, IteratorAggregate
{
private $objects;
public function attach(Identifiable $object) {
$id = $object->getId();
$this->objects[$id] = $object;
}
public function count() {
return count($this->objects);
}
public function has($id) {
return isset($this->objects[$id]);
}
public function getById($id) {
if ($this->has($id)) {
return $this->objects[$id];
}
throw new InvalidArgumentException(sprintf("No object is identifiable for %d", $id));
}
public function getIterator() {
return new ArrayIterator($this->objects);
}
}
// create the collection
$collection = new IdentifiableCollection();
// fill the collection with some objects (ID 1 - 20)
foreach(range(1, 20) as $id) {
$collection->attach(new MyObject($id));
}
// test if an id exists and return object
$id = 2;
var_dump($collection->has($id), $collection->getById($id));
// iterate over the collection
foreach ($collection as $object) {
var_dump($object);
}
This collection class only offers to attach objects, not remove them but you can extend that as needed. It's also possible to extend from an existing class like ArrayObject or SplObjectStorage if you want to reuse existing functionality. An example is given in another answer in a somewhat related question:
Array of objects within class in PHP
This is not really a problem but more of a question.
My question is how it's 'supposed to be' programmed.
It might not be too clear to explain, though.
So my question is; do I have to make multiple methods to retrieve data from a database?
For example, I have a table called FRUITS.
It contains the ID, name and date the fruit was added.
Now I want to get the name of the fruit based on a given ID, and later on in the script I want to get the date of the fruit as well.
Should I make one method such as get_fruit($id) which returns both the name and date, or two separate methods get_name($id) and get_date($id)?
Thanks in advance.
You should use one object which would contain all the required data. For example:
class Fruit {
protected ... variables;
public function getId() {...}
public function getDate() {...}
...
}
Also implementing __set and __get would be nice example of using full php potential.
You also may implement save() method (or extend database row class, such as Zend_Db_Table_Row.
So whole code would look like:
$fruit = $model->getFruid( 7); // $id = 7 :)
echo $fruit->id; // would call internally $fruit->__get( 'id')
echo $fruit->date;
// And modification:
$fruit->data = '2011-08-07';
$fruit->save();
EDIT: using separate methods to load certain data is useful (only?) when you need to load large amount of data (such as long texts) which is required only on one place in your code and would affect performance.
EDIT 2: (answer to comment):
__get and __set are called when you try to access undefined property of an object, for example:
class Foo {
public $bar;
public function __get( $name){
echo $name "\n";
return 'This value was loaded';
}
}
// Try to use
Foo $foo;
echo $foo->bar . "\n";
echo $foo->foo . "\n";
There are two "large" approaches to this that I know about:
// First use __get and __set to access internal array containing data
class DbObject {
protected $data = array();
public function __get( $propertyName){
// Cannot use isset because of null values
if( !array_key_exits( $propertyName,$this->data)){
throw new Exception( 'Undefined key....');
}
return $this->data[ $propertyName];
}
// Don't forget to implement __set, __isset
}
// Second try to call getters and setter, such as:
class DbObject {
public function getId() {
return $this->id;
}
public function __get( $propertyName){
$methodName = 'get' . ucfirst( $propertyName);
if( !method_exits( array( $this, $methodName)){
throw new Exception( 'Undefined key....');
}
return $this->$methodName();
}
}
To sum up... First approach is easy to implement, is fully automatized... You don't need large amount of code and sources would be pretty much the same for every class. The second approach required more coding, but gives you a better control. For example:
public function setDate( $date){
$this->date = date( 'Y-m-d h:i:s', strtotime( $date));
}
But on the other hand, you can do this with first approach:
class Fruit extends DbObject {
public function __set( $key, $val){
switch( $key){
case 'date':
return $this->setDate( $val);
default:
return parent::__set( $key, $val);
}
}
}
Or you can use total combination and check for getter/setter first and than try to access property directly...
here is the code how you can use the one function to get different field's value.
function get_fruit($id,$field = ''){
$sql = "select * from table_name where id = $id";
$result = mysql_fetch_object(mysql_query($sql));
if($field != ''){
return $result->$field;
}else{
return $result;
}
}
echo get_fruit(1,'field_name');
class data_retrieve
{
public $tablename;
public $dbname;
public $fieldset;
public $data_array;
public $num_rows
function __construct()
{
$this->tablename='junk';
$this->dbname='test';
$this->fieldset=array('junk_id');
}
function getData($where_str)
{
$this->data_array= array();
global $dbconnect, $query;
if($dbconnect ==0 )
{
echo" Already Connected \n ";
$dbconnect=db_connect("objectdb") or die("cannot connect");
}
$where_str;
if(empty($where_str))
{
$where_str=NULL;
}
else
{
$where_str= "where". $where_str ;
}
$query= "select * from $this->tablename $where_str";
$record= mysql_query($query) or die($query);
$recNo=mysql_num_rows($record);
$this->num_rows=$recNo;
while($row= mysql_fetch_assoc($record))
{
$this->data_array[]=$row;
}
mysql_free_result($record);
return $this->data_array;
}
class fruit extends data_retrieve
{
function __construct()
{
parent::__construct();
$this->tablename='fruit';
$this->fieldset=array('fruit_id','fruit_name','date');
}
}
then
in your file create a fruit object like
$str="fruit_id=5";
$fruit_data = new fruit();
$records=$fruit_data->getData($str);
to display
foreach($records as $row )
{
print <<< HERE
<label class='table_content' > $row[fruit_id]</label>
<label class='table_content' > $row[fruit_name]</label>
<label class='table_content' > $row[date]</label>
HERE;
}