I'm in need of calling a function located in a class stored in app/lib directory before every action (ie. in preExecute action)
In that function I do need a $this pointer from an action.
I can do it in a static way - Classname::functionname(), however it results with a PHP warning, which I want to avoid.
For now I handle it with
ini_set('display_errors', '0'); # don't show any errors...
error_reporting(E_ALL | E_STRICT); # ...but do log them
but it's an ugly way..
How can I do it in a dynamic way?
===============
EDIT:
as requested - I add a code to show what am I actually doing with that $this pointer
...
$c = new Criteria();
$c->add(ArticlePeer::PUBLISHED, true);
$articles = ArticlePeer::doSelect($c);
$this->articles = $articles;
...
Yes, I know that I can put this Select in preExecute action in every module and it will be working fine. However - I have many modules, and as I have a set of actions that are all the same for all those modules, so putting them in one procedure and just calling that function would be the smartest way...
Especially when it comes to maintaining the app - It's only one place to change code, instead of a dozen of them...
In your class inside your app/lib folder, you simply have to return $articles:
$c = new Criteria();
$c->add(ArticlePeer::PUBLISHED, true);
return ArticlePeer::doSelect($c);
Then, inside your preExecute():
public function preExecute()
{
$this->articles = Classname::functionname();
}
If you have to return multiple value, you can do it using an array:
$c = new Criteria();
$c->add(ArticlePeer::PUBLISHED, true);
$articles = ArticlePeer::doSelect($c);
$c = new Criteria();
$c->add(BookPeer::PUBLISHED, true);
$books = BookPeer::doSelect($c);
return array(
'articles' => $articles,
'books' => $books,
);
End then, use this array to populate your variable inside your action:
public function preExecute()
{
$data = Classname::functionname();
$this->articles = $data['articles'];
$this->books = $data['books'];
}
You cannot change the value of $this in a method. Never.
Instead, look at the class how you can access $articles from outside, there should be something like getArticles(). It looks like the class is a Propel model, those have generated getters like that.
Dependent on what your end goal is, there might be better solutions than instantiating this model, calling the mysterious method and then getting the attribute. After all, classes are not just collections of functions and should not be treated like that. Not repeating code is good, but it has to be done with some sense.
Related
I'm trying to write a code where I could run a function based on it's previous variables. I don't know how to explain any better but a sample will do. I'm trying to do something like this:
<?php
$agric = new Agriculture;
$newplant = $agric-> setClass('plant');
$newanimal = $agric->setClass('animal');
$agric->getAll(); // returns null
$newplant->setProperties($plant1_data); //uses plant
$newanimal->setProperties($animal1_data); // uses animal
$newplant->setProperties($plant2_data); //uses plant
$newanimal->setProperties($animal2_data); // uses animal
$newplant->getAll(); // returns all plants array
$newanimal->getAll(); // returns all animals array
$agric->getAll(); // returns both plants array and animals
?>
So, In one form, the new variables calls the setClass in order to work and everytime it is is called they use their setClass method to know which type of argument they should use to run their code. I know I could do this differently but I seem to love this approach. Any help will do. Thanks in advance
Thanks #everyone.. I finally got the answer to that. So the reason why I'm giving it to #Dragos is because phone factory seems to be the best approach to it. So, all I did was to create two classes. One is Agriculture which has a method to get the properties of what was called using "getAll()" and the Other is HandleAgric which also has "getAll()". The HandleAgric has its own setClass which is static and every time it is called, it instantiates a new Agriculture with its default parameter. So, Both Classes will have setClass() and getAll() as method. Something like this
<?php
class HandleAgric{
private static $Agriculture;
public function __construct(){
self::setClass();
}
function __call($method,$args){
$self = new self;
if(method_exists($self::$Agriculture, $method)){
$call = call_user_func(array($self::$Agriculture, $method));
return $call;
}
return trigger_error("Error Found!!! Aborting");
}
public static function setClass($class=null){
self::$Agriculture = new Agriculture;
$call = self::$Agriculture->setClass($class);
return $call;
}
}
//Example of usage
$Agric = new HandleAgric();
$Plant = $Agric::setClass("plant");
$Animal = $Agric::setClass("animal");
$Plant->setProperties($arrayList);
$Animal->setProperties($arrayList);
# $Plant->getAll() //return plants properties in array;
# $Animal->getAll() //return Animal properties in array;
# $Agric->getAll() //return Agric (both plant and animal) properties in array;
?>
I believe this to be much better.
For your example to work, the method setClass from Agriculture must work like a factory: it instantiates a class based on the parameter and returns the object.
Agriculture class must also keep all the objects instantiated inside setClass method in its own internal array, so when getAll method is called, it iterates through each object and executes their own getAll methods.
I'm trying to use the moodle Data Manipulation API to get the grades from students to analyse. But when I use the function get_record() inside of another function that I've created I get the message of the tittle. I don't know why the fuction works in the main and don't work inside a function.Any idea? I'm new in php and moodle manipulation, so take easy on me.
<?php
function get_all_quiz ($courseid) {
$quizesid = [];
$quiz = $DB->get_record('moodle.quiz', array('id'=>$courseid));
$quizesid = $quiz.id;
return $quizesid;
}
global $DB;
define('CLI_SCRIPT', true);
require '../../var/www/moodle/config.php';
$coursetest = 3;
$studentgrades = [];
$quizes = get_all_quiz($coursetest);
?>
There are a few things you need to fix in this function:
You need to add global $DB; inside the function
The quiz database table is called 'quiz', not 'moodle.quiz' (Moodle config handles connecting to the correct database)
PHP uses '->' operator to access object properties, not '.' (which is used for concatenation), so it is $quiz->id not, $quiz.id
If you are wanting to return all quizzes, you should call $DB->get_records(), not $DB->get_record() (which returns just 1 record and outputs debugging warnings if more than one is found).
If you want the quizzes for a particular course, then you should match the 'course' field in the quiz record, not the 'id' field (which is the ID of the quiz, not the course).
So the function should probably look like this:
function get_all_quiz($courseid) {
global $DB;
return $DB->get_records('quiz', array('course' => $courseid));
}
I wrote a vcard class with Phalcon in PHP. The vCard Model is initialized like this.
// Inside the BS_VCard class
public function initialize(){
$this->hasMany("id","BS_VCardElement","vCardId",array(
"alias" => "elements",
'foreignKey' => array(
'action' => Phalcon\Mvc\Model\Relation::ACTION_CASCADE
)
));
}
Its elements are initialized like this
// Inside the BS_VCardElement class
public function initialize(){
$this->belongsTo("vCardId","BS_VCard","id",array("alias" => "vCard"));
...
}
If a user reads a vCard and adds another element, it doesn't work as expected. To simplify the use I added some fascade methods like this
public function addDateOfBirth($date){
$element = new BS_VCardElement();
$element->setName("BDAY");
$element->addValue($date);
// This doesn't work
$this->elements[] = $element;
}
The Docs/Storing related records do not explain how to append fresh data like this to the related table.
I also tried this
$this->elements[] = array_merge($this->elements,array($element));
But the save method seems to ignore the added element. Save() returns true.
This question has been asked a couple of months ago but since I ran into a similar issue I decided to share my results anyway.
And here's what I found. Lower case aliases ('elements') don't seem to work whereas upper case aliases ('Elements') do.
To add one element you can do this;
$this->Elements = $element;
To add multiple elements you can do this;
$elements = array($element1, $element2);
$this->Elements = $elements;
After that you have to save the vcard before accessing the elements again. If you don't, phalcon will just return a result set with only the elements already in the database. (Not sure if this can be changed somehow.)
And here's the documentation (where all this is not mentioned): http://docs.phalconphp.com/en/latest/reference/models.html#storing-related-records
According to the Phalcon source code, the Resultset object is immutible.
/**
* Resultsets cannot be changed. It has only been implemented to
* meet the definition of the ArrayAccess interface
*
* #param int index
* #param \Phalcon\Mvc\ModelInterface value
*/
public function offsetSet(var index, var value)
{
throw new Exception("Cursor is an immutable ArrayAccess object");
}
It appears that replacing the element with an array is the only way to implement an "append" or modification of the resultset (other than delete which IS supported).
Of course this breaks the \Phalcon\Mvc\Model::_preSaveRelatedRecords() because the function ignores the class properties and refetches the related from the Model Manager (and resets the model::$element attribute at the end).
I feel frustrated by this because appending objects to a collection seems like a very common task and not having a clear method in which to add new items to a parent seems like a design flaw.
I think related elements might have some magic functionality invoked when you set the properties, so simply using $this->elements[] (evidently) doesn't work. Perhaps try re-setting the entire variable:
public function addDateOfBirth($date){
$element = new BS_VCardElement();
$element->setName("BDAY");
$element->addValue($date);
$elements = $this->elements;
$elements[] = $element;
$this->elements = $elements;
}
I'm using CI, and for every main page (for example contents) I load a specific css file in the constructor using :
$this->load->vars(array('add_css'=>array('contents')));
In my views, I check if the add_css array exists or not, if yes, I load the contents.css. Now in my controller file (in this case contents.php), I have lots of methods, and while all of them will always load the additional contents.css , specific methods will have (if any) their own add_css too, for example when I'm on method review, I want to load additional rating.css. What I did was :
$data['add_css'] = array('rating');
But it doesn't work because the rating.css overwrites the vars in the constructor.
So is there any way I can push the array? I've tried array_push($data['add_css'],'rating') but it doesn't work.
Thanks
The most elegant way I can think off would be this:
1. In your controller or if you have MY_Controller(it's much better) add new protected (so it is available from all controllers) property that will keep all loaded css files.
protected $_addCss = array();
2. Add new method for setting new css files to this array:
protected function _addCssFile($file)
{
$this->_addCss[] = $file;
return $this; //So you can chain it when adding but not necessary
}
3. New method for manual loading of all added css files.
protected function _loadCssFiles()
{
//Load the vars then reset the property to empty array again
$this->load->vars(array('add_css' => $this->_addCss));
$this->_addCss = array();
return $this;
}
4. In your application controller you can use it like this:
public function __construct()
{
parent::__construct();
$this->_addCssFile('contents');
}
public function test()
{
//Chain the methods so you can add additional css files
$this->_addCssFile('contents')
->_addCssFile('rating')
->_loadCssFiles()
->load->view('test');
}
5. In your view:
echo '<pre>';
print_r($add_css);
echo '</pre>';
Result:
Array
(
[0] => contents
[1] => contents
[2] => rating
)
I had a similar problem. Read what's in the array, write to another array, push new info to it, overwrite the second array to data.
I made a class. I give it some objects (mostly retreived database rows) as input, and tell it which fields it has to show, and which buttons I want. Then it renders a very nice html table! It's pretty awesome, I think.
$ot = new ObjectTable();
$ot->objects = $some_objects;
$ot->fields = array('id','name','description','image');
$ot->buttons = array('edit','delete');
$ot->render();
However, I also want to be able to manipulate the data it shows. For example, i want to be able to truncate the 'description' field. Or to display an image thumbnail (instead of 'picture.jpg' as text). I don't know how to pass these functions to the class. Perhaps something like:
$ot->functions = array(null,null,'truncate','thumbnail');
But then I don't know how to convert these strings to run some actual code, or how to pass parameters.
There must be a nice way to do what I want. How?
Check this question and the answer is:
As of PHP5.3 you could use closures
or functors to pass methods
around. Prior to that, you could write
an anonymous function with
create_function(), but that is
rather awkward.
But what you are trying to achieve is best solved by passing Filter Objects to your renderer though. All filters should use the same method, so you can use it in a Strategy Pattern way, e.g. write an interface first:
interface Filter
{
public function filter($value);
}
Then write your filters implementing the interface
class TruncateFilter implements Filter
{
protected $_maxLength;
public function __construct($maxLength = 50)
{
$this->_maxLength = (int) $maxLength;
}
public function filter($value)
{
return substr(0, $this->_maxLength, $value) . '…';
}
}
Give your ObjectTable a method to accept filters
public function addFilter($field, Filter $filter)
{
if(in_array($field, $this->fields)) {
$this->_filters[$field][] = $filter;
}
return $this;
}
And when you do your ObjectTable instantiation, use
$ot = new ObjectTable();
$ot->objects = $some_objects;
$ot->fields = array('id','name','description','image');
$ot->addFilter('description', new TruncateFilter)
->addFilter('name', new TruncateFilter(10))
->addFilter('image', new ThumbnailFilter);
Then modify your render() method to check if there is any Filters set for the fields you are rendering and call the filter() method on them.
public function render()
{
foreach($this->fields as $field) {
$fieldValue = // get field value somehow
if(isset($this->filters[$field])) {
foreach($this->filters[$field] as $filter) {
$fieldValue = $filter->filter($fieldValue)
}
}
// render filtered value
}
}
This way you can add infinite filters.
PHP has a pseudo-type called "callback", which is actually an ugly closure in disguise. You can call such callbacks using call_user_func() or call_user_func_array():
$callback = 'strlen';
echo call_user_func($callback, '123');
// will output 3 (unless you're using a strange encoding)
You are looking for create_function().
However, creating functions on runtime and adding them to a class doesn't sound right to me. It's likely to become a maintenance nightmare very quickly. There are better ways to achieve what you want. What kind of manipulation would the functions to to the data? How about storing them in a "tools" class and connecting that with the table object when needed?
$functions = array(null,null,'truncate','thumbnail');
$function_1 = $functions[3];
$my_string = 'string to truncate';
$result = call_user_func($functions[2], $my_string);
If you want to pass multiple parameters, use call_user_func_array instead.
You might also want to explore call_user_func, which allows you to call a function based on a string representing its name.