I am trying to catch a certain findBy call (with afterFind) where:
if $results is empty (or the value you are trying to find is nonexistent), but the parameter value is found on another table, then it will modify $results to be valid
Some controller action got this:
$this->User->findByUsername("Bingo"); // yes, username Bingo doesnt exist on users table
User model:
function afterFind($results, $primary){
if(empty($results)) {
if(in_array($findbyparameter, array("Bingo", "Bingo1", "Bingo2"))) {
// modify $results
}
}
}
The problem is, how do I get $findbyparameter?
Thanks! All help will be appreciated!
I am not using these convenience methods, but you can pass the variable as Model property like this:
//where you search
$this->User->searchPhrase = "Bingo";
findByUsername($this->User->searchPhrase);
//Model
function afterFind($results, $primary){
if(empty($results)) {
if(in_array($this->searchPhrase, array("Bingo", "Bingo1", "Bingo2"))) {
// modify $results
}
}
}
It's not the prettiest method, but I guess it would work. Try to print_r($this) in afterFind method and see if you can spot somewhere the phrase which you search. I believe it's passed in the condition's array.
Perhaps a custom find type is what you're looking for. Custom find types have two states: before and after.
In the before you would setup your condition, and in the after you would check your data and modify if necessary. In both states you will have access to the query options.
Setting up custom finds is slightly different in 1.x and 2.x (you haven't mentioned which version you're using), so you can look up the specifics in the book.
In short, you would add add your the find type into the $findMethods property of the model and then add the corresponding method name to your model. Say you call your custom find type 'byUsername'
protected function _findByUsername($state, $query, $results = array()) {
if ($state === 'before') {
// add your condition to the query,
return $query;
} elseif ($state === 'after') {
// modify $results if you need to
return $results;
}
}
And you would call it via $this->User->find('byUsername', array('username' => $username));
In $query you would have the key 'username' which you can add to the conditions key of $query. In both states, you would have access to $query['username'].
Related
Let's imagine I have two Models:
A list of users User
A list of marbles Marble which belongs to one User
I would like to fetch all the existing marbles with api/marbles and only my marbles with api/user/marbles. The idea is to avoid a route named like api/marbles?owned=true
In my API routes I have this:
Route::get('marbles', 'MarbleController#index');
Route::get('user/marbles', 'MarbleController#index(true)');
Then in my MarbleController:
class MarbleControllerextends Controller
{
function index($owned = false) {
return $owned ? Marble::where('user_id', Auth::id())->get() : Marble::all();
}
}
Unfortunately the MarbleController#index(true) doesn't really work because (true) will not be accepted by Laravel not populate the optional $owned variable.
Is there a way to avoid defining a new method such as Route::get('user/marbles', 'MarbleController#owned');
function owned() {
return $this->index(true);
}
Route::get('marbles/{me?}', 'MarbleController#index'); will work fine.
Here me is an optional parameter. If you omit it, it will take false otherwise true as it's value.
I'm looking for a way to make a dynamic & global model filter in Laravel.
I'm imagining a function like the following in my User.php model:
public function filter() {
return ($someVariable === true);
}
Whenever I do a query using Eloquent's query builder, I only want users to show up in the collection when the filter above returns true. I would have thought a feature like that existed, but a quick look at the documentation suggests otherwise. Or did I miss it?
I believe what you're looking for is Query Scopes.
They are methods that may be defined in a global or local context, that mutate the current query for a given model.
https://laravel.com/docs/5.5/eloquent#query-scopes
For example:
Lets say I have a database table called "Teams" and it has a column on it called "Wins." If I wanted to retrieve all Teams that had a number of Wins between Aand B I could write the following Local scope method on the teams model:
public function scopeWinsBetween($query, int $min, int $max)
{
return $query->whereBetween('wins', $min, $max);
}
And it could be invoked as such:
$teams = Teams::winsBetween(50, 100)->get();
I think you could use Collection macro but you will need to suffix all your eloquent get(); to get()->userDynamicFilter();
Collection::macro('userDynamicFilter', function () {
//$expected = ...
return $this->filter(function ($value) use($expected) {
return $value == $expected;
});
});
Thanks. For now I've simply added a post filter option to the models using the following code:
// Apply a post filter on the model collection
$data = $data->filter(function($modelObject) {
return (method_exists($modelObject, 'postFilter')) ? $modelObject->postFilter($modelObject) : true;
});
in Illuminate/Database/Eloquent/Builder.php's get() function, after creating the collection. This allows me to add a function postFilter($model) into my model which returns either true or false.
Probably not the cleanest solution but a working one for now.
I have a Laravel model acl_groups that has a JSON column inherits. What should I do, the "laravel way" to query the inherited groups when checking if a group can do something? The rights are stored in another JSON column, allow/deny so I can just do a in_array to check a single group if they have access.
On your model you can set a getter
public function getInheritsAttribute($v)
{
return $v ? json_decode($v, true) : [];
}
OR
if you dont want a getter you can try a pseudo getter
public function getPseudoAttribute()
{
return $this->inherits ? json_decode($this->inherits, true) : [];
}
Kind of maybe did mistake on second one.
And on other model the same thing
so when you call $item->inherits = you will get an array
First you may try to prepare the array like removing same keys or values
and after just check
if (array_key_exists('thing_to_check', $item->inherits)) {
return true;
}
This is not a working code, it is just an idea how you can do you.
Take a look at Cartalyst Sentinel how they check the permissions for groups and users.
I have a model in Yii that contains an array of another model type. I am then trying to validate that no duplicate emails are filled out in a form, where you can fill out for n number of persons at the same time.
My current approach is to trigger a custom validation of the "outer" model that holds all the entrants, however, that model is not accessible in the view, only the array of entrants is, and if I then trigger the error on the "outer" model, it will not be displayed to the user. Therefore I would like to trigger it for the first entrant that violates the rule, but how do I go about doing that?
My code that attempts this, looks like this so far:
/*
* Custom validation rule to hinder the same e-mail being used twice.
*/
public function noRepeatingEmails($attribute, $params)
{
if (!isset($attribute)) return;
$emails = array();
foreach($this->$attribute as $user)
{
if (isset($user) && strlen(trim($user->email)) != 0)
{
$emailToAdd = strtolower(trim($user->email));
if (in_array($emailToAdd, $emails))
{
$this->addError($user, '<my error message>');
return;
}
else
{
$emails[] = $emailToAdd;
}
}
}
}
This only results in a code 500 error though:
Illegal offset type
I presume that is because it is looking for the property "user" in my model, rather than adding an error to "$user" object.
How do I best accomplish this?
I have a .NET background, so I am probably doing loads wrong here however.
If I understood correctly from your comment, you want to validate your model before saving it. For this purpose, CActiveRecord provides beforeSave() method. You need to put this method inside your model:
protected function beforeSave()
{
if(parent::beforeSave())
{
if(/* Your validation goes here*/)
return true;
else
return false
}
else
return false;
}
When the result of this method is true, save() method will be called. Otherwise save() method won't be called and therefore no record will be saved into your database.
I am writing a method which can call any method from any class (this process is dynamic).
In my method, I need to find out what type is the returned value, based on the returned value type,I will proceed on to the next step.
For example:
<?php
function identifyReturnType($className, $methodName) {
$result = $className->$methodName();
//Here I need to find out the $result type
}
?>
I have many classes where methods return bool, string, int etc.
and there are a few methods which do not return anything, those methods set the values in object or the object has resource pointer :
<?php
function getCategories() {
$this->query("SELECT * FROM categories");
}
function getItems() {
$this->query("SELECT * FROM items");
$this->getValues();
}
?>
PHP gettype($var) method finds out what is the value type but for this, my method must return a value. I have cases (as I explained above) where method just sets the query object.
Please share your ideas.
Thank you so much.
This really depends on your implementation. Some follow architecture where every function will return data as array. Even for query returned data is returned in small chunks of array. That is completely on how you optimize or write your script. Say you are getting all contacts and if you have say 10,000 contacts in DB and you return all in an array, thats a bad idea. Rather use pagination and return in small numbers if you want the function to return data as array.
I have had this issue, where we have a big web application written in PHP/Mysql. Over the time we have thousands of functions across different classes. Now we have to develop a REST API which will have different functionality. The main problem was we do not have used different functions to return query object, some to return array, some to return Boolean and so on. The API should return data as JSON. Now we have to choice use the existing code for different functionality or re-write new code for the API. The 2nd choice is more expensive so we are left with first choice. But the problem as I mentioned is far from over the methods will return different type and do we need to really write more codes to check which function is called and if the say function "xyz()" is called and we know its returning query object then loop through it generate array and then json. No thats a bad idea and will take a lot of effort and its better to write seperate code then.
So we follow the following approach.
Our api call looks like
www.api.oursite.com/api/v1/Resource/Method?param=....
Now we catch the Resource and Method where resource is a Class name and Method is a method name for that Class.
so we know we have to call Resource->Method()
Now we have a class called ResourceMethodMap.class.php and it contains the array as
static $resource_method_map = array(
"Users"=>array(
"getUserInfo"=> // gets the user info
array(
"return"=>"array",
"accessToken"=>true
)
),
....
...
)
So the API request processing code does something like
public function call_method($resource = "",$method=""){
if($resource == "") $resource = $this->get_resource();
if($method == "") $method = $this->get_api_method();
if (class_exists($resource)) {
$resource_obj = new $resource();
// Parse the method params as array
$param_array = $this->parse_method_params($resource,$method);
if(false !== $param_array){
$result = call_user_func_array(array($resource_obj, $method), $param_array);
}else{
$result = $resource_obj->$method() ;
}
return $this->process_return_data($resource,$method,$result,$resource_obj);
}else{
$this->setMessage("Invalid Resource");
return false ;
}
}
Here the function process_return_data() will do the returned data conversion as
function process_return_data($resource,$method,$ret_val,$resource_obj = NULL){
if(array_key_exists("return",ResourceMethodMap::$resource_method_map[$resource][$method])){
$return_type = ResourceMethodMap::$resource_method_map[$resource][$method]["return"];
$return_array= array();
switch($return_type){
case 'boolean':
if(false === $ret_val){
return false ;
}else{
if(is_array($ret_val)){
return $ret_val ;
}elseif(true === $ret_val){
return $ret_val ;
}else{
$return_array[] = $ret_val ;
return $return_array ;
}
}
break;
case 'array':
return $ret_val ;
break;
}
.....
}
}
So Yes it completely on the developer how they want their data to be returned. The above example is just one real time scenario how we have implemented.
I have posted the complete code her http://codepad.org/MPY1gVed have look
If i understood your question right you can do this by passing in an argument as a reference.
Here's an example i made for you, if it is any help.
http://php.net/manual/en/language.references.pass.php
Another solution can be to return an array with both the return value and the type.
Do you real need a method to call other methods? You could just instantiate the class and call it manually
In adittion i would recommend checking like so:
if(is_callable($className, $methodName)){
$className->$methodName();
}