I've managed to over-ride the default methods for a custom query in my model as suggested elsewhere,
function paginate($conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array())
and
function paginateCount($conditions = null, $recursive = 0, $extra = array())
Unfortunately this approach over-rides all pagination for this model, and affects other pagination elsewhere. I found some code which may help where I could select whether I wanted the custom pagination used based on a variable e.g.
In my model
var $useCustom = false;
function paginateCount($conditions = null, $recursive = 0, $extra = array())
{
if(!$this->useCustom)
return parent::paginateCount($conditions, $recursive);
// code to handle custom paginate count here
}
I have found that using this method gives me an error,
Fatal error: Call to undefined method
AppModel::paginateCount() in....
What am I doing wrong? I assume that I would also need similar code in the paginate function as well? Am I also correct in thinking that I can set this variable in my controller i.e. $this->useCustom = 'true';
After a bit of delving into the code I found that the methods of paginateCount and paginate do not exist in the Model, or anywhere else for that matter, which is why I could not call them. The solution was copy the code from the main controller, which tests for the existence of the over-ride
For those that would like a similar solution use the following in paginateCount
if(!$this->useCustom)
{
$parameters = compact('conditions');
if ($recursive != $this->recursive) {
$parameters['recursive'] = $recursive;
}
$count = $this->find('count', array_merge($parameters, $extra));
return $count;
} else {
custom method...
}
and in paginate use
if(!$this->useCustom)
{
$parameters = compact('conditions', 'fields', 'order', 'limit', 'page');
if ($recursive != $this->recursive) {
$parameters['recursive'] = $recursive;
}
$results = $this->find('all', array_merge($parameters, $extra));
return $results;
} else {
custom method...
}
Hope this helps someone else.
I think you need the public keyword before your function to use the scope resolution operator in this way.
i.e. public function paginateCount(....
Related
Is there a way in PHP to use a function which has optional parameters in its declaration where I do not have to pass an optional arguments which already have values declared and just pass the next argument(s) which have different values that are further down the parameter list.
Assuming I have a function that has 4 arguments, 2 mandatory, 2 optional. I don't want to use null values for the optional arguments. In usage, there are cases where I want to use the function and the value of the 3rd argument is the same as the default value but the value of the 4th argument is different.
I am looking for a not so verbose solution that allows me to just pass the argument that differs from the default value without considering the order in the function declaration.
createUrl($host, $path, $protocol='http', $port = 80) {
//doSomething
return $protocol.'://'.$host.':'.$port.'/'.$path;
}
I find myself repeating declaring variables so that I could use a function i.e to use $port, I redeclare $protocol with the default value outside the function scope i.e
$protocol = "http";
$port = 8080;
Is there any way to pass the 2nd optional parameter($port) without passing $protocol and it would "automatically" fill in the default value of $protocol i.e
getHttpUrl($server, $path, $port);
This is possible in some languages like Dart in the form of Named Optional parameters.See usage in this SO thread. Is their a similar solution in PHP
You could potentially use a variadic function for this.
Example:
<?php
function myFunc(...$args){
$sum = 0;
foreach ($args as $arg) {
$sum += $arg;
}
return $sum;
}
Documentation:
http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list
PHP doesn't allow at this state to call functions parameters in the order we want.Maybe in the future it will.However you can easily achieve your purpose by using an associative array as the only argument, and then define, the default parameter in the function.For the call you will need to pass an array with only the values which interest you.This array will be merged with the default array.You can even implement required parameters and call them in any order you want.
example:
function mysweetcode($argument){
$required=['first'];//specify required parameters here
$default=['first'=>0,'second'=>1,'third'=>2];//define all parameters with their default values here
$missing=[];
if(!is_array($argument)) return false;
$argument=array_intersect_key($argument,$default);
foreach($required as $k=>$v){//check for missing required parameters
if(!isset($argument[$v]))
$missing[]=$v;
}
if(!empty($missing)){// if required are missing trigger or throw error according to the PHP version
$cm=count($missing);
if (version_compare(PHP_VERSION, '7.0.0') < 0) {
trigger_error(call_user_func_array('sprintf',
array_merge(array('Required '.(($cm>1)?'parameters:':'parameter:').
str_repeat('%s,',$cm).(($cm>1)?' are':' is').' missing'),$missing)),
E_USER_ERROR);
}else{
throw new Error(call_user_func_array('sprintf',array_merge(
array('Required '.(($cm>1)?'parameters:':'parameter:').
str_repeat('%s',$cm).(($cm>1)?' are':' is').' missing'),$missing)));
}
}
$default=array_merge($default,$argument);//assign given values to parameters
extract($default);/*extract the parameters to allow further checking
and other operations in the function or method*/
unset($required,$missing,$argument,$default,$k,$v);//gain some space
//then you can use $first,$second,$third in your code
return $first+$second+$third;
}
var_dump(mysweetcode(['first'=>9,'third'=>8]));//the output is 18
var_dump(mysweetcode(['third'=>8]));//this throws Error on PHP7 and trigger fatal error on PHP5
You can check a live working code here
Well, this should work:
function myFunc($arg1, $arg2, $arg3=null, $arg4= null){
if ( is_null( $arg3 ) && is_null( $arg4 ) {
$arg3 = 3;
$arg4 = 4;
} else if ( is_null( $arg4 ) ) {
$arg4 = $arg3;
$arg3 = 3;
}
echo $arg1 + $arg2 + $arg3 + $arg4;
}
However I suggest you to rethink your problem (as a whole) because this is not a very good idea.
You could refactor this to use a parameter object; this way, you could include the default parameters in this object and set them in any order (with a trade-off of more verbose code). As an example using your above code,
<?php
class AdditionParameters
{
private $arg1 = 0;
private $arg2 = 0;
private $arg3 = 3;
private $arg4 = 4;
public function getArg1() { return $this->arg1; }
public function getArg2() { return $this->arg2; }
public function getArg3() { return $this->arg3; }
public function getArg4() { return $this->arg4; }
public function setArg1($value) { $this->arg1 = $value; return $this; }
public function setArg2($value) { $this->arg2 = $value; return $this; }
public function setArg3($value) { $this->arg3 = $value; return $this; }
public function setArg4($value) { $this->arg4 = $value; return $this; }
}
From there, you could simply call the function while passing in this new object.
function myFunc(AdditionParameters $request) {
return $request->getArg1()
+ $request->getArg2()
+ $request->getArg3()
+ $request->getArg4();
}
echo myFunc((new AdditionParameters)->setArg1(1)->setArg2(2)->setArg4(6));
// or echo myFunc((new AdditionParameters)->setArg1(1)->setArg4(6)->setArg2(2));
Otherwise, PHP doesn't allow you to have named optional parameters. (e.g. myFunc(1, 2, DEFAULT, 4);)
You have the response in your question, you can declare your function like
function myFunc($arg1, $arg2, $arg3 = null, $arg4 = null){
//here you check if the $arg3 and $arg4 are null
}
then you call your function using
myFunc($arg1, $arg2);
There is no such way in PHP(like in python for example).
You have to use some tricks in order to do that but will not always work.
For example:
// creates instances of a class with $properties.
// if $times is bigger than 1 an array of instances will be returned instead.(this is just an example function)
function getInstance($class, $properties = [], $times = 1){
//my code
}
$user = getInstance("User", ["name" => "John"]); // get one instance
$users = getInstance("User", ["name" => "John"],2); // get two instances.
If you want to use the function without passing the $parameters argument, like this:
$users = getInstance("User",2);
you can change the function to:
// creates instances of a class with $properties.
// if times is bigger than 1 an array of instances will be returned instead.
function getInstance($class, $properties = [], $times = 1){
if(is_numberic($properties)){
$times = $properties;
$properties = [];
}
//my code
}
Of course, this strategy will work only if you parameters have different types.
PS. This method is use in the Laravel Framework a lot. From there I got the inspiration.
This is modified from one of the answers and allows arguments to be added in any order using associative arrays for the optional arguments
function createUrl($host, $path, $argument = []){
$optionalArgs = [
'protocol'=>'http',
'port'=>80];
if( !is_array ($argument) ) return false;
$argument = array_intersect_key($argument,$optionalArgs);
$optionalArgs = array_merge($optionalArgs,$argument);
extract($optionalArgs);
return $protocol.'://'.$host.':'.$port.'/'.$path;
}
//No arguments with function call
echo createUrl ("www.example.com",'no-arguments');
// returns http://www.example.com:80/no-arguments
$argList=['port'=>9000];
//using port argument only
echo createUrl ("www.example.com",'one-args', $argList);
//returns http://www.example.com:9000/one-args
//Use of both parameters as arguments. Order does not matter
$argList2 = ['port'=>8080,'protocol'=>'ftp'];
echo createUrl ("www.example.com",'two-args-no-order', $argList2);
//returns ftp://www.example.com:8080/two-args-no-order
As of version 8.0, PHP now has named arguments. If you name the arguments when calling the function, you can pass them in any order and you can skip earlier default values without having to explicitly pass a value for them.
For example:
function createUrl($host, $path, $protocol = 'http', $port = 80)
{
return "$protocol://$host:$port/$path";
}
createUrl(host: 'example.com', path: 'foo/bar', port: 8080);
// returns: "http://example.com:8080/foo/bar"
I have the function/method below inside a class that I'm creating and I'm just wondering what's the best way to handle empty/null arguments.
For example, in the following example, if I wanted to just set just the category when calling the function, I would need to use:
$data = $class->get_top_headlines( null, 'technology' );
Is there any way of calling the function more efficiently? I know I could pass the arguments as an array instead, but just wondered if there's any way of doing something like:
$data = $class->get_top_headlines( $category='technology' ); and automatically leaving the other arguments as their default of null?
public function get_top_headlines( $query=null, $category=null, $country=null, $sources=null, $page_size=null, $page=null ){
$url = $this->api_url . $this->endpoint_top_headlines;
$params = array();
if ( $query !== null ){
$params['q'] = urlencode( $query );
}
if ( $category !== null ){
$params['category'] = $category;
}
if ( $country !== null ){
$params['country'] = $country;
}
if ( $sources !== null ){
$params['sources'] = $sources;
}
if ( $page_size !== null ){
$params['pageSize'] = $page_size;
}
if ( $page !== null ){
$params['page'] = $page;
}
$params['apiKey'] = $this->api_key;
$url_query = http_build_query( $params );
$url = $url . '?' . $url_query;
echo $url;
$obj = $this->get_response( $url );
return $obj;
}
Try passing an array, and then using an array_merge
$data = $class->get_top_headlines(['category' => 'technology']);
Then in your function, have an array of defaults, then do your merge.
$settings = array_merge($settings, $passedInArray);
http://php.net/manual/en/function.array-merge.php
I think
(null, 'technology' );
might be less actual coding but a different solution might be to use OOP. You said it's already a method of a class so you could do something like:
$obj = new thatClass;
$obj->technology = $technology;
$obj->get_top_headlines();
in the Class:
Class thatClass{
$technology = null;
$category = null;
$query = null;
//...
public function get_top_headlines(){
if ( $this->query !== null ){
$params['q'] = urlencode( $this->query );
}
if ( $this->category !== null ){
$params['category'] = $this->category;
}
if ( $this->technology !== null ){
$params['technology'] = $this->technology;
}
//method code..
}
//class code..
}
The problem with this approach is if you need to call that same function again passing a different parameter in the same class instance, depending on your application you might need to manually set back to null the previous parameter (now an object attribute)
I would solve this problem by creating a new class or data structure which will encapsulate all the logic of validating and generating the URL and then use it everywhere I need it.
Here's a sample class.
class HeadLineParameters
{
private $params = [];
public function setQuery($query)
{
// validate/transform query data
$this->params['q'] = urlencode($query);
return $this;
}
public function setCategory($category)
{
// validate/transform category data
$this->params['category'] = $category;
return $this;
}
public function generateUrl()
{
return http_build_query( $this->params );
}
}
$params = new HeadLineParameters;
$params->setQuery($query)
->setCategory($category);
You just pass one argument and you know that it's just an instance of HeadLineParameters.
$class->get_top_headlines($params);
This solution doesn't pollute your current class with unnecessary state or fields. It is easy to test, and it has only one job. You can extend it easily, you can set default values, you can also validate it as you like.
Edit: Why you shouldn't add more fields to your current class?
If you add more fields to your current class you'll be breaking the single responsibility principle, and any method of this class can change these fields too. It shouldn't be a problem if these fields really belong there and more methods require them. This is fine if you are using OOP.
I am not sure what other people think about passing associated arrays to functions, but they are hard to handle if you have no documentation available. I have had trouble with them when reading some external code, and most of time I wasn't sure what's the data I was dealing with.
Alright I have a GeneralModel written in CodeIgniter and a friend asked me if I could convert it into Laravel 4.2 for him. I was working on this and I think I have most of it correct but I am getting stuck at the select statement.
In CodeIgniter I have the following:
public function getData($table, $multiple = 1, $field = FALSE, $val = FALSE){
if($field != FALSE){
// WHERE in case of FIELD / VAL :)
$this->db->where($field, $val);
}
$query = $this->db->get($table);
if($multiple == 1){
// Multiple rows
return $query->result_array();
} else {
// One row
return $query->row_array();
}
}
Does anyone here knows how I can convert this function into Laravel 4.2 syntax?
I currently have:
public function getData($table, $multiple = 1, $field = FALSE, $val = FALSE){
$result = DB::table($table);
}
I got stuck pretty quickly since I have no idea how I can achieve the same in Laravel 4.2 with splitting up the sections of the query like I did with CodeIgniter.
You can chain methods in the same way:
public function getData($table, $multiple = 1, $field = FALSE, $val = FALSE)
{
$query = DB::table($table);
if ($field != FALSE) {
// WHERE in case of FIELD / VAL :)
$query->where($field, $val);
}
if ($multiple)
return $query->get();
else
return $query->first();
}
Laravel is similar in that you can use the Fluent Query Builder to build your queries in multiple stages prior to actually making the query. Once you know this, the translation is pretty straightforward:
public function getData($table, $multiple = 1, $field = FALSE, $val = FALSE)
{
$query = DB::table($table);
if($field){
// WHERE in case of FIELD / VAL :)
$query = $query->where($field, $val);
}
if($multiple) {
return $query->get();
}
return $query->first();
}
I don't think it's really good practice relying on Fluent though inside of an Eloquent model, but there are cases where that can't be helped. If the current objective is to convert the project to Laravel, there's probably calling code relying on the fact that this method exists. Converting the function to use Eloquent rather than Fluent will change the function's signature and cause other parts of the code to break, but it would look like this:
public function getData($multiple = true, $field = false, $val = false)
{
$query = $this;
if($field) {
$query = $query->where($field, $val);
}
if($multiple) {
return $query->get();
}
return $query->first();
}
The calling code itself can be modified to do the exact same function in Laravel like this:
// Instead of...
$result = $model->getData(1, 'field', 'value');
// You can do this:
$result = $model->where('field', 'value')->get();
// Or this if you'd rather not have multiple:
$result = $model->where('field', 'value')->first();
Using this function inside Eloquent (IMHO) in the long run doesn't really save you much, and instead is mostly just clutter.
I'm currently working with the medoo.php framework, and although I would normally use their ticket area on github, it appears that no one actually uses that... so...
At any rate, when I'm running one of my files which uses "require" to call the framework, I get the following error:
Warning: Cannot use a scalar value as an array in /home/..../public_html/projects/friendcodes/medoo.min.php on line 759
However, when I inspect the code (the below is lines 752 to 764), I see that it is in fact supposed to check if $where is not set, and if it isn't, make it an array - however this php error begs to differ.
I'm guessing that $where is being set as a variable somewhere else, that's not an array, but there are over 100 occurrences of the variable in the framework, and 830 lines of code, which you probably don't want to see. (Let me know in a comment and I'll add it - again, this is directly from medoo's most two recent updates/releases.)
public function get($table, $columns, $where = null)
{
if (!isset($where))
{
$where = array();
}
$where['LIMIT'] = 1;
$data = $this->select($table, $columns, $where);
return isset($data[0]) ? $data[0] : false;
}
My main question is - How do I rectify this problem without breaking something in this framework which is extremely complex (for my level, at any rate)
Update: How silly of me! I found the problem. Just as people suggested, I was calling $where wrong.
I was calling it with:
$accountinfo = $database->get('xf_user_field_value', ['field_value'], 1);
Instead of
$accountinfo = $database->get('xf_user_field_value', ['field_value'], ["user_id"=>1]);
(Where the third arg is $where) Thanks for the help guys!
Right, first things first, we need to find out what is calling get that shouldn't be. WHICH IS THE ENTIRE PROBLEM. The problem isn't the function itself, the problem is something is calling it using an argument for $where which isn't an array. Changing a library to fix one faulty call is ridiculous.
Step 1: Temporarily edit the get function to include a print_r of the $where variable.
public function get($table, $columns, $where = null)
{
if(isset($where)) print_r($where);
if (!isset($where))
{
$where = array();
}
$where['LIMIT'] = 1;
$data = $this->select($table, $columns, $where);
return isset($data[0]) ? $data[0] : false;
}
This will show us before the error prints the value of $where, which will help you find the malformed get call.
If this fails, try using PHP's built-in backtrace to try to find the issue:
public function get($table, $columns, $where = null)
{
if(isset($where)) print_r(debug_backtrace());
if (!isset($where))
{
$where = array();
}
$where['LIMIT'] = 1;
$data = $this->select($table, $columns, $where);
return isset($data[0]) ? $data[0] : false;
}
The ->get() method is not called properly.
Cannot use a scalar value as an array
That warning is shown if $where is either true, a numeric value or a resource. Valid method calls include:
->get('table', '*')
->get('table', '*', array('WHERE' => 'foo = "bar"'))
Check the manual and fix your code.
EDIT 3: try moving $where['LIMIT'] = 1; inside of the isset statement, since you wouldn't want to pass LIMIT 1 to the query constructor if $where is passed by reference.
DISCLAIMER I have no knowledge of the medoo framework.
public function get($table, $columns, $where = null)
{
if (is_null($where))
{
$where = array('LIMIT'=>1);
}
$data = $this->select($table, $columns, $where);
return isset($data[0]) ? $data[0] : false;
}
I have a static method 'findAll' on a model which basically gets all rows with certain criteria. This method works fine and I can call it using:
$m::findAll();
Where $m is the model name as a variable. I can output this and it returns correct results. However, when assigning this to a variable in the Zend_View object, as:
$this->view->viewvariable = $m::findAll();
I get the error:
Zend_Db_Table_Exception: Too many
columns for the primary key
Any ideas why?
Find all function:
final public static function findAll($where = false, array $options = array()) {
$object = new static();
if (!empty($options)) $options = array_merge($object->options, $options);
else $options = $object->options;
$run = $object->buildDefaultSelect($where, $options);
$rows = $run->fetchAll();
if ($options['asObject'] == true) {
$result = array();
foreach ($rows as $r) {
$class = new static();
$class->setInfo($r);
$result[] = $class;
}
return $result;
} else {
if (count($rows) > 0) return $rows;
else return array();
}
}
Note: This function works fine everywhere apart from when assigning to a view variable. If I run the below (not assigning it to a view variable), it shows the correct array data.
var_dump($m::findAll($module['where'], $module['options']));
exit;
In my view (I have replaced the actual name with viewvariable for the sake of this post):
<?php foreach($this->viewvariable as $item) { ?>
//Do some echoing of data in $item
//Close foreach
I doubt the issue is with Zend_View. It's hard to tell without seeing your code, but my guess is that findAll() is using the Zend_Table_Db find() function incorrectly.
To my knowledge, the only place that throws that exception is the find() function on Zend_Db_Table_Abstract.
Perhaps, inside the findAll() function (or in a function it calls) you're doing one of these:
$zendDbTable->find(1,2) //is looking for a compound key
$zendDbTable->find(array(1,2)) //is looking for two rows
When you really want the opposite.