Need a INT from Eloquent. Getting JSON string - php

I have this query:
static function findIdOnName($pageName){
return Fanpages::select('id')
->where('url', '=', $pageName)
->get();
}
Response: (when done print_r)
[{"id":17}]
I just want the INT (in this case 17) I searched the interwebs for it, but I can't find anthing about it. Randomly tried adding ->toString() etc to the query, but so far, no good.

Your code returns a Collection with a single Model (or multiple models if there are more matching the where clause), while the method you need is pluck:
return Fanpages::where('url', '=', $pageName)->pluck('id');
// returns INT 17
as it returns value for column id of the first row matching WHERE clause.

If you do not return a view with data, then Laravel will automatically convert your data into json. In order to accomplish what you want you can simply do something like
$data = Fanpages::select('id')->where('url', '=', $pageName)->get();
die($data->id);
However, exiting the application like this isn't recommended. You should either keep the json response and work with that, or send the data to a basic blade template.

Related

Most efficient way to modify an Eloquent query such that it always returns the empty collection?

I frequently encounter the programming pattern which takes a Eloquent Query object and adds a filter condition to it. (You may think of a filter as a scope, if you like.) This mostly happens in the context of authorization, whether an authenticated user may or may not see certain subsets of a result.
The pattern looks like this:
use Illuminate\Database\Eloquent\Builder;
public function applyFilter(Builder $query): Builder {
$model = $query->getModel();
if (!($model instanceof MyModel)) {
throw new \InvalidArgumentException('the given query does not query for <MyModel>');
}
$user = Auth::user();
return $query->where(function (Builder $subquery) use ($user) {
// Add some filter conditions to the query
// Typically those depend on the currently authenticated user,
// privileges of the user and and some properties of the model.
});
}
Sometimes it happens to be already clear that the query must not return any result, just from inspecting the $user object or some other property like the state of the application, even without querying for the actual models. In those cases, the filter function above adds a condition to the query which always evaluates to false like ->where('1', '=', '2') or similar. Although, this does the trick and works perfectly, it feels wrong. Eloquent still queries the DB back-end and receives the (expected) empty result.
Primary question:
Is there a way, how one could tell Eloquent that a query is known to return the empty result and that no actual query must be made, but that ->get (and friends) simply return an empty collection? I am thinking of a method like $query->toNoOp() or similar.
Further question:
Moreover, also consider the case where the construction above is used as part of a more complex query and happens to end up in a sub-query. For example consider the following case:
$relatedModels = MyRelatedModel::query()
->where(...)
->whereHas('my_model', $q => applyFilter($q))
->get();
At the moment, this construction results into the rather complicated SQL query
SELECT *
FROM my_related_models
WHERE
...
AND
EXISTS (
SELECT *
FROM my_models
WHERE
my_models.id = my_related_models.model_id
AND
1 = 2
);
Although, this again returns the expected empty result, it would be much better if the fact that the inner query is a "no-op" could be propagated to the outer query. In other words, if a sub-query is used within an EXIST clause (constructed by ->whereHas and friends) and this sub-query is known to be a "no-op" query, then the outer query should also become a "no-op" query.
If you know the query is invalid, you should not even run the query / execute it. Just make your API return empty array.
public function index() {
if (! $this->shouldRunQuery(auth()->user())) {
return response()->json([]);
}
return response()->json(Model::query()->get());
}
In your sub question, you can execute queries depending on conditions with when(), this is thou handled by Laravel and not by SQL.
Model::query()
->when($this->shouldRunQuery(auth()->user()), function ($query) {
// your complex query
})
->get();
In general, you seem very fascinating about solving the problem in SQL, instead of handling conditions and states even before the SQL is executed. I have seen a lot, but never seen anyone execute this approach.

Laravel Query - whereJsonContains() reverse

It's possible to write in Laravel query reverse for whereJsonContains() query, something like: whereJsonDoesntContains()?
I have problem with data filtering in Model. This code works perfectly:
$query->whereHas('attributes', function($q) use ($attribute)
{
$q->where('attribute_product.attribute_id', $attribute->id)
->whereJsonContains('attribute_product.values', $someValue);
});
Now I want to create another query that returns products whereHas attributes with ID and JSON array in values column doesn't contain my variable.
Do you have any idea to do this?
Yes, there is a function just like that: whereJsonDoesntContain

Laravel get single column result as string

Hello I would like to know is it possible to get column value as string. Instead of array in array:
Current query: Number::limit('1000')->get(['number'])->toArray()
The result at the moment is this:
Preferable result:
Before your toArray() call, add pluck('number'):
$result = Number::limit('1000')->get(['number'])->pluck('number')->toArray();
That's it! This will pluck just the number attributes from your result collection, and give you a single-level array.
The reason this works, is because you are getting a Collection back from get():
All multi-result sets returned by Eloquent are an instance of the Illuminate\Database\Eloquent\Collection object, including results retrieved via the get method or accessed via a relationship.
And the pluck method:
https://laravel.com/docs/5.1/collections#method-pluck
Update
Another, even more succinct method provided by #wunch in the comments:
$result = Number::limit('1000')->lists('number')->toArray();

how to get the where clause in string format using CakePHP3 ORM?

In CakePHP3, there is a ORM that helps with building queries.
From the documentation, I can see that
$query = $articles->find(); // build a query that has not run yet
$query->where(['id' => 1]); // Return the same query object
So in this case, I want the string
WHERE `articles`.`id` = 1
After much googling, I found out that there is a way to return just the where clause of a query object.
$query->where(['id' => 1])->clause('where'); // Return the where clause in the form of a QueryExpression
More googling leads me to find out how to get the QueryExpression to spit out string representation
$query->where(['id' => 1])->clause('where')->sql($valueBinder); // Return the where clause in string format
Here is my problem. I don't know what the $valueBinder is supposed to look like. I don't know how to initialize it.
I am also happy not to use ValueBinder as long as I can get the where clause in string format using CakePHP 3 ORM and in the right SQL dialect. Please assume I am using MySQL.
Please advise.
EDIT
I tried to use $query->valueBinder() as the $valueBinder.
It is empty and does not contain the associated c:0 to the value 1.
To directly answer your question, you can get the SQL for any clause this way:
$binder = new \Cake\ORM\ValueBinder();
$query->clause('where')->sql($binder);
That will return the SQL with the correct placeholders, not with the values to be used. The values live in the $binder variable and are used for statement objects.
As I can see, you only wanted to preserve the internal structure of the where clause to pass it to another query in a different request. Your solution is fine, but I'd like to add that you can also encode a full conditions tree from an existing query:
$where = serialize($query->clause('where'));
$anotherQuery->where(unserialize($where)); // A query in another request
In any case, you need to be careful with what you are unserializing as taking it directly from user input will certainly lead to security problems.
You can choose to omit this param if you like. Please see http://api.cakephp.org/3.0/class-Cake.Database.Query.html#_sql
In addition, you can use the Query member function traverse($visitor, $parts) to isolate the where clause. $visitor is a function that takes a value and a clause. You define the behavior of $visitor. $parts is an array of clause names. I suggest passing array('where') into this param.
My workaround is that I store the conditions in json string format.
Using the same example, what I do is
$data['conditions'] = json_encode(['Articles.id' => 1]); // encode into JSON string
$this->DynamicRules->patchEntity($dynamicRule, $data); // use in edit action of DynamicRulesController
then when I need to reuse the conditions, I do:
$articlesTable = TableRegistry::get('Articles');
$query = $articlesTable->find(); // new query for Articles
$rule = json_decode($dynamicRule->conditions, true); // get back the conditions in associative array format
$query->where($rule); // re-assign the conditions back
This got me what I ultimately wanted.

Simplifying the output of a Laravel model query

Suppose I run the following and need to know the results in an easy to examine format:
$orders = Order::where('status', $value);
How would I show something that doesn't display the entire model object, only the records retrieved, when I do dd($orders)?
You may try this:
$orders = Order::where('status', $value)->get();
dd($orders->toArray()); // Outputs only an array of records retrieved from db
There is also toJson() to convert into json string but it's possible to get json representation of the data by returning it from the controller/function, for example:
return Order::where('status', $value)->get(); // Output will be in json format
Instead of dding, just return the model directly from your route/controller:
return Order::where('status', $value)->get();
This will automatically convert it to JSON.
For even better inspection, install the JSON View Chrome extension; it'll format your JSON nicely, and allow you to inspect it beautifully.

Categories