I'm having an issue with using Laravels put() function, as I want put JSON content in this one single scenario.
$datatable->GroupsCollection = $datatable->GroupsCollection->put($job, '{"grade":'.$grade.'}' );
But when trying to create 'fake' JSON, the inserted value will be:
{\"grade\":'VALUE_OF_$GRADE'}
I've tried using str_replace() and stripslashes() to cut out the backwardslashes, but no bueno.
I've Googled around, and reading something about a cast was needed in the Model.
So I put in this:
protected $casts = [
'dvalue' => 'array',
];
This result in breaking existing functionality of the code.
public function getGroupsCollectionAttribute()
{
return collect($this->dvalue ? $this->dvalue['groups'] : null);
}
public function setGroupsCollectionAttribute($value)
{
$currentValue = $this->dvalue ?? new Collection();
$this->dvalue['groups'] = $currentValue->$value;
}
I 'fixed' the get, but I'm not sure how I should format the 'set' function with this new cast and setting it to an array.
Worth to notice is that we have mixed content in the DB-rows, so it's not always JSON.
Any easier way to go around this?
Ending up fixing it by simply creating an array like this:
$grade_json = array("grade" => $grade);
$datatable->GroupsCollection = $datatable->GroupsCollection->put($job, $grade_json);
Related
I have a Laravel site I am modifying, but there are some parts of the PHP code I don't quite understand, which are "array objects" or "object arrays". You see, I don't even know what to call them and so can't find a tutorial or basic data on it. Below is the code that I am dealing with:
private function parseMetric($result, $view)
{
$data = collect([]);
$result->each(function($item) use ($data, $view) {
if (isset($item->metric->{$view})) {
$data->push((object)[
'label' => $item->metric->{$view},
'value' => $item->metric->count
]);
}
});
...
From what I can tell, this creates an object out of $result. If I json_encode this and echo it out I get this:
[{"label":"1k-25k","value":14229},
{"label":"1mm+","value":1281},
{"label":"25k-50k","value":398},
{"label":"50k-75k","value":493},
{"label":"75k-100k","value":3848},
{"label":"100k-150k","value":9921},
{"label":"150k-200k","value":4949},
{"label":"200k-250k","value":3883},
{"label":"250k-300k","value":2685},
{"label":"300k-350k","value":2744},
{"label":"350k-500k","value":4526},
{"label":"500k-1mm","value":8690}]
Now this is obviously an array of arrays... or is it? Is it an array of objects? Or is it an object containing arrays? But the most important question is, how do I access and move or change the individual objects/arrays in this object? For example, I want to take the second object/array, which is:
{"label":"1mm+","value":1281}
and move it to the end. How do I do that? How do I find it? I used the following piece of code to find it which is pretty clunky:
$pos = strpos(json_encode($result), '1mm+');
if($pos){
Log::debug('Enrich 73, I found it!!!!!!!!!!!!!!!!!!!!!!!!!!!');
}
And once I find it, how do I move that array/object to the end of the whole object?
And finally, where can I find some kind of tutorial, or documentation, that describes this construct and how to work with it?
There is no need to json_encode the data. Since the data is an instance of Laravel Collection, you can manipulate it like so
$item = $data->firstWhere('label', '1mm+'); // get the item
$data = $data->filter(fn($value, $key) => $value->label !== '1mm+') // remove $item from $data
->push($item); // move $item to the end of data
Acording to Laravel documnentation for Collections, you can try something like this :
To find index of element with name = "1mm+" :
$index = $datas->search(function ($item, $key) {
return $item['name'] == "1mm+";
});
to get an element at a given index :
$element = $datas->get($index);
to Move element at index 3 to the end :
$index = 3
$elementToMove = $data->splice($index, 1);
$datas->push($elementToMove);
Here is a link to the document used : https://laravel.com/docs/8.x/collections
I wish to give a list of options as an argument to a function.
The Ideal Scenario: Named Parameters
If PHP has named parameters it would be done like so:
function setOptions($title, $url, $public = true, $placeholder = "type here...") {
...
}
setOptions($title = "Hello World", $url = "example.com", $placeholder = "hi");
Unfortunately PHP does not have named parameters (please tell me if PHP7 is planned to have some as a comment).
The solution everyone else is using: Associative Array
Most PHP scripts I have seen use an alternative array approach like so:
function setOptions($options) {
...
}
setOptions(array(
'title' => "Hello World",
'url' => "example.com",
'placeholder' => "hi"
));
Drawbacks of Associative Array Approach
Although this works fine, there are the following drawbacks:
The user does not benefit from autocompletion (taking a long time to write)
The user can easily makes mistakes in spellings
The don't know what options is available, so may frequently revert back to documentation
Is there a better way?
Is there a better way that can address these issues (either in current PHP or PHP7 or maybe even hacklang(?)).
In Hack, you can use Shapes. Shapes define a structure for associative arrays so that things can be autocompleted (depending on IDE support) and spelling mistakes are picked up by the type checker.
For instance, your example could be reworked like:
function setOptions(shape(
'title' => string,
'url' => string,
'public' => ?bool,
'placeholder' => ?string,
) $options) {
$title = $options['title'];
$url = $options['url'];
$public = Shapes::idx($options, 'public', true);
$placeholder = Shapes::idx($options, 'placeholder', 'type here...');
...
}
setOptions(shape(
'title' => 'Hello World',
'url' => 'example.com',
'placeholder' => 'hi',
));
This marks title and url to both be required options and public and placeholder are optional (all nullable types in shapes are considered to be optional). Shapes::idx is then used to get the value provided, or the default value (the third argument) if a value was not passed in.
Solution: Using fluent setters
A potential solution I have found to this problem is to use classes and fluent setters like so:
class PostOptions {
protected
$title,
$url,
$public = TRUE,
$placeholder = "type here..."; //Default Values can be set here
static function getInstance(): PostOptions {
return new self();
}
public function setTitle($title) {
$this->title = $title;
return $this;
}
public function setUrl($url) {
$this->url = $url;
return $this;
}
public function setPublic($public) {
$this->public = $public;
return $this;
}
public function setPlaceholder($placeholder) {
$this->placeholder = $placeholder;
return $this;
}
}
You can then send the options like so:
function setOptions(PostOptions $postOptions) {
//...
}
setOptions(
PostOptions::getInstance()
->setTitle("Hello World")
->setUrl("example.com")
->setPlaceholder("hi")
);
Doing it quickly! (This looks long)
Although this may look long, it can actually be implemented VERY quickly using IDE tools.
e.g. In InteliJ or PHPStorm, just type ALT+INS > Select setters > Select the fields you want to set and check the checkbox for fluent setters > click OK
Why Fluent Setters? Why Not just make all the fields public?
Using public fields is a LOT slower. This is because fluent setters can make use of chained methods, whilst the public fields way must be written like this:
$options = new PostOptions();
$options->title = "hello";
$options->placeholder = "...";
$options->url "..."
setOptions($options);
Which is a lot more typing compared to the proposed solution
Why is this better?
It's faster in IDE's when using autocomplete than the array approach
Unlikely to make mistakes in spellings (thanks to autocomplete)
Easy to see what options is available (again thanks to autocomplete)
Can give individual documentation for individual fields using PHPDoc
Can use nested options more easily e.g. If you had a list of options, and that option also had more list of options
Other OOP advantages e.g. Inheritance & Abstract Classes
How much faster is this approach?
I implemented a quick class for Wordpress labels array in: https://codex.wordpress.org/Function_Reference/register_post_type
I found that setting a property for each value (with the documentation next to you on a 2nd monitor) that the fluent setters approach is approximately 25% faster than the array approach thanks to autocomplete! However, if the documentation was not next to you, I expect this approach will far exceed 25%, as discovery of options is much quicker!
Alternative approaches are welcome
Declaration from array
This is how I normally declare my class structure. The only drawback is that it takes a while longer to write, but it allows optional parameters, defaults values, etc.
public static $defaults = array(
'user_id' => null,
'username' => null,
'avatar' => null,
'email' => null,
'description' => null,
);
public function __construct(array $args = array()) {
$this->dbc = Database::connection();
$defaults = self::$defaults;
$args = array_merge($defaults, $args);
//Assign the object properites
$this->user_id = (is_numeric($args['user_id'])) ? $args['user_id'] : null;
$this->username = $args['username'];
$this->avatar = AVATAR_DIR . $args['avatar'];
$this->email = $args['email'];
$this->description = $args['description'];
}
This way, you can declare an object like $x = new User(), and it will work perfectly fine. Let's say you've only selected a few columns from your SQL statement. You can make the keys in the public static $defaults into the same name as the columns you've selected, that way to instantiate your object, you can easily do:
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
$object = new User($row);
The array_merge takes care of having any extraneous keys that you don't need in the argument they provided. If you need to change options, you can declare them the same way for __construct() with a default array and array_merge to catch arguments and mimic named parameters and defaults values (like in Python)
With Syntactic: https://github.com/topclaudy/php-syntactic
you can just do:
function foo($a = 1, $b = 2, $c = 3, $d = 4){
return $a * $b * $c * $d;
}
And call it with the arguments you want:
//Call with argument b only
echo s('foo')->in('b', 5)->out(); //Outputs 60
//Call with argument a and argument at index/position 1 (b),
echo s('foo')->in('a', 7)->in(1, 5)->out(); //Outputs 420
//Call with argument c only through dynamic method
echo s('foo')->c(9)->out(); //Outputs 72
If U have that much parameters I'd think about creating an object that you'll pass to class instead of n parameters and every parameter is one field there. In constructor you put required parameters and this is then clean solution.
I'm wrapping a model function in Yii 1.8 that has the signature:
public save($runValidation=true, array $attributes=NULL)
With a function:
public xSave(array $params)
That allows the addition of a flag and optional message that causes the wrapper function to throw an Exception in the case that the delegated save() function returns false.
I toyed with the idea of overwriting save() with:
public save(
$runValidation=true,
array $attributes=NULL,
$exception_on_error=false,
$exception_message=false
)
but would like to allow the specification of the last two parameters independently from the first and like the idea of allowing the extra readability of passing in an array with string keys.
I have so far:
/**
* Enhanced save function.
* Delegates to standard model save to allow exceptions to be thrown
* in the case where the model was not saved.
**/
public function xSave(array $params=array()){
$_params=array(
'run_validation'=>true,
'attributes'=> null,
'exception_on_failure'=>false,
'exception_message'=>false,
);
array_merge($_params, $params);
// Call the save method.
$is_saved=$this->save($_params['run_validation'],$_params['attributes']);
// Throw exception to if flag set and model not saved.
if($_params['exception_on_failure'] && !$is_saved){
// If no exception message was passed in, use the default.
if($_params['exception_message'] === false){
throw new CException('
Could not '.($this->isNewRecord()?'create':'update').' '.get_class($this).';
Errors: '.CJSON::encode($this->errors)
);
}
// Else throw using the one provided.
throw new CException($exception_message);
}
// Return result of standard save method.
return $is_saved;
}
Firstly I'd like to know if this is a sensible choice, as I may well use it for other parts of the system. I'm currently not too worried about the typing of the parameters although I agree this could be an issue in the future.
Secondly I would also like the ability to throw an exception in the case that $params has a key that is not defined in $_params with a message specifying that key, and would like to do this as part of the array_merge if possible.
To 1), yes, passing arrays is the usual lame workaround in languages that don't support named arguments (see jQuery etc). With the new array syntax, this is even almost readable:
$some->save([
$runValidation => true,
$attributes => ['foo', 'bar']
]);
Inside a function, you can use extract to avoid ugly $params[foobar] references.
For better taste though, persuade #NikiC to get this patch ready ))
To 2), if you plan to use argument arrays systematically, consider a helper function like this:
function parseArgs($args, $defaults) {
foreach($args as $k => $v) {
if(!array_key_exists($k, $defaults))
throw new Exception("invalid argument: $k");
// might want to add some type checking, like
// if(gettype($v) != gettype($defaults[$k])) bang!
}
return $args + $defaults;
}
Usage:
public function xSave(array $params=array()){
extract(parseArgs($params, [
'run_validation'=>true,
'attributes'=> null,
'exception_on_failure'=>false,
'exception_message'=>false,
]));
if ($run_validation)
etc....
The decision to either use single parameters or parameter-arrays is opinion based. It depends on the situation. At least I would keep the design consistent across the whole project.
To decide if there had been unknown parameters passed, you can use array_diff():
$a = array(
'test' => 'foo',
'name' => 'bar'
);
$b = array(
'test' => 'foo',
'name' => 'bar',
'abcd' => '123'
);
$d = array_diff(
array_keys($b), array_keys($a)
);
echo "The following keys can't be understood: " . implode(', ', $d) . PHP_EOL;
However, I would skip that check as it will not "harm" if there are unknown parameters.
Now I admit I am slightly new to laravel still, but to me this just does not make sense. The model that goes along with this table contains only 2 functions, both containing a relationship statement.
I am using Laravel4, mysql, php 5.5
Any ideas are welcome :D
The database record-definitions are for both DATETIME, allow null and no default value (changed that after the screenshots)
the $challenge variable is part of the data I pass on to the view like so:
$challenges = Auth::user()->challenges;
$data['challenges'] = $challenges;
return View::make("challenges/topic", $data);
and in the view I use
#foreach($challenges as $challenge)
read the challenge values (I am aware I cant echo like that without php tags or {{ }}, just easier to explain)
echo gettype($challenge->deadline) // results in string
echo gettype($challenge->created_at) // results in object
Depends on how you access it, if you do:
Route::any('test', ['as' => 'test', function()
{
$a = Article::first();
var_dump( gettype($a->created_at) );
$a = DB::table('articles')->first();
var_dump( gettype($a->created_at) );
}]);
You will get:
string 'object' (length=6) /// This is Eloquent
string 'string' (length=6) /// This is the QueryBuilder directly accessing your table
I have a model that runs a query with a bunch of conditions in the SQL. As a result, the model needs to accept a lot of parameters, i.e:
this->model_name->method($param1, $param2, ... )
On the model side, I typically set this up as
function method($param1 = NULL, $param2 = NULL, ... )
Each of those parameters is optional, and use cases will vary around the app. So my question is: at what point (if ever) does it make sense to start passing these parameters to the method via an array, a la:
$params = [
'param1' => 'whatever',
'param2' => 'whatever',
...
]
this->model_name->method($params)
With the end goal being, I suppose, cleaner code, and less instances of method(null, null, null, null, $param) unless that's an okay thing to do.
Most answers have been supportive of the array method (which, generally speaking, I would also agree with), but I'll play devil's advocate and list some negatives:
Documentation is less clear
Most methods of documenting functions/methods will list the parameters of that function individually. For example, a function with a basic DocBlock will look like this:
/**
* A function that accepts an array of params
* #param array $param_array An array of key=>value arguments
*/
function accept_array($param_array = array('key1' => 'first_val', 'key2' => 'second_val')) {
var_dump($param_array);
}
Note how the DocBlock doesn't directly support individual parts of the $param_array, just the array as a whole. In contrast, listing all the arguments individually looks like this:
/**
* A function that 'normal' params
* #param string $key1 First argument
* #param string $key2 Second argument
*/
function accept_normal($key1 = 'first_val', $key2 = 'second_val') {
echo $key1;
echo $key2;
}
This is also a problem if you expect your functions to be fairly self-documenting, as in the first example you're not required to actually list your expected arguments in the function itself.
Default values may not work as expected
'As expected' is probably a bit of a loaded phrase (and this is probably one of the more obvious problems), but take the following:
function accept_array($param_array = array('key1' => 'first_val', 'key2' => 'second_val')) {
var_dump($param_array);
}
accept_array(array('key2' => 'a_different_val'));
Some may expect the var_dump to include the default value of key1 and the new value of key2, but the whole array is replaced, meaning you will need to remember to set default values for each key manually in each function, like so:
function accept_array($param_array = array()) {
if (!isset($param_array['key1'])) { $param_array['key1'] = 'first_val'; }
if (!isset($param_array['key2'])) { $param_array['key2'] = 'second_val'; }
var_dump($param_array);
}
accept_array(array('key2' => 'a_different_val'));
No automatic filtering
Listing the arguments the 'normal' way also gives you a built-in set of filters. Take for example this quick and dirty user search:
/**
* We want to allow searching for users by user_id or email only!
* #param array $param_array
*/
function find_user($param_array = array('user_id' => 0, 'email' => '')) {
foreach ($param_array as $field => $value) {
$this->db->or_where($field, $value);
}
$this->db->get('users');
}
find_user(array('first_name' => 'Joe', 'last_name' => 'Bloggs'));
Without manually adding some 'accepted keys' type validation on the $param_array, a call to the find_user() function can essentially use whatever fields it likes. The simpler version would obviously look like this:
/**
* We want to allow searching for users by user_id or email only!
* #param int $user_id
* #param string $email
*/
function find_user($user_id = 0, $email = '') {
$this->db->or_where('user_id', $user_id);
$this->db->or_where('email', $email);
$this->db->get('users');
}
// No way for me to submit any other fields, they'll just fail when they get to the query
find_user('Joe', 'Bloggs'));
I accept some of these are a bit entry-level and there's probably many more that I missed (feel free to comment with more and I'll copy them into the reply with credit), but hopefully there's enough there to make people think twice about automatically using the 'array method' without thinking about manual validation and documentation etc.
Passing an array of parameters provides a better option for self-documenting your code.
When I use many parameters, I often find myself using a style like:
// do_something_model($enable_option1,$enable_option2,$enable_option3)
do_something_model(FALSE, TRUE, FALSE)
where I carry a comment line with the parameter names to remind myself of how I am
using the model.
In such a case, using an array with meaningfully named keys provides a useful mnemonic.
More recently, I am also using more wrapper functions. For example, I may have my
basic model method do get all my data from a table and this method will have a few
options.
I then define a new method that does a specific task and then invoke the basic method within it using the correct options.
Footnote
I find that if my methods have "too many options", it is better to rethink the purpose of the method and to break it up into two or more specialized methods that are easier to use.
I would recommend the array version as well. Symfony2 also uses this pattern a lot, for instance in rendring templates, creating form classes and creating http responses in general. You just have to make sure you cleanly document all possible parameters.
You could go either route, but an array would definitely keep your methods cleaner. It makes perfect sense to pass the parameters as an array.