I want to add a sql filter where('comment_id', '=', 1) to php code
$datas = $this->model->ADD HERE->orderBy('created_at', 'DESC')->paginate(15);
Trying to add the string to code take me hours. How to make it?
Here is my code:
CommentResource.php passing the sql filter as string parameter.
<?php
class CommentResource extends BaseResource
{
public function index()
{
$filter = "where('comment_id', '=', 1)";
return parent::index_filter($filter);
}
CommentResource.php
<?php
class BaseResource extends Controller
{
protected function index_filter($filter)
{
$datas = $this->model->ADD HERE->orderBy('created_at', 'DESC')->paginate(15);
return view($this->resourceView.'.index')->with('datas', $datas);
}
}
As I understand you want to use different types of where as filters in your queries. That's why you want to make them dynamic. I would suggest the following solution for your task:
<?php
class CommentResource extends BaseResource
{
public function index()
{
$filter = [ 'operator' => 'where', 'args' => ['comment_id', '=', 1]];
return parent::index_filter($filter);
}
<?php
class BaseResource extends Controller
{
protected function index_filter($filter)
{
$where = $filter['operator'];
$args = $filter['args'];
$datas = $this->model->$where(...$args)->orderBy('created_at', 'DESC')->paginate(15);
return view($this->resourceView.'.index')->with('datas', $datas);
}
}
However, it will work starting from Php5.6+ because of oeprator ...
I am not sure if I got your requirements correctly, but if you rewrite index_filter to accept field and value separately, then you may user a regular where() from laravel:
protected function index_filter($field,$value)
{
$datas = $this->model->where($field,$value)->orderBy('created_at', 'DESC')->paginate(15);
return view($this->resourceView.'.index')->with('datas', $datas);
}
You can find the docs here. In case you really need more flexibility:
protected function index_filter($filter)
{
$datas = $this->model->whereRaw($filter)->orderBy('created_at', 'DESC')->paginate(15);
return view($this->resourceView.'.index')->with('datas', $datas);
}
Have in mind though that this is really dangerous, as you expose the possibility to inject malicious code, it should be definitely properly escaped beforehand.
My latest code works right. I'll post here.
<?php
class CommentResource extends BaseResource
{
public function index()
{
$options = [
'filters'=>[
[ 'operator' => 'where',
'args' => [
[ 'article_id', '=', $article_id ],
[ 'comment_id', '=', $comment_id ],
// add filter args...
],
],
// add filter operators here...
],
'sorts' => [
'column' => $sortColumn, // change sort column...
'order' => $sortOrder, // change sort order...
],
];
return parent::index_filter($options);
}
<?php
class BaseResource extends Controller
{
protected function index_filter($options, $number=15)
{
$result = $this->model;
foreach ($options['filters'] as $filter) {
$operator = $filter['operator'];
$args = $filter['args'];
$result = $result->$operator($args);
}
if ( $options['sorts'] != [] ) {
$column = $options['sorts']['column'];
$order = $options['sorts']['order'];
$result = $result->orderBy($column, $order);
}
return $result->paginate($number);
}
}
The reason I change ...$args to $args is, when 'args' has more than on value, for example,
'args' => [
[ 'article_id', '=', $article_id ],
[ 'comment_id', '=', $comment_id ],
// add filter args...
],
...$args will change 'args' to one array, but $args will remain 'args' as nest array, which is the operator 'where' want.
Related
hi Can this code be refactor?
public function all(array $attributes)
{
$rates = $this->model->query()->where([
['able_type', $attributes['type']],
['able_id', $attributes['type_id']]
])
->get(['rate']);
return [
'count' => $rates->count(),
'average' => $rates->avg('rate')
];
}
public function show($attributes)
{
$result = $this->model->query()->where([
['user_id', $attributes['user_id']],
['able_type', $attributes['type']],
['able_id', $attributes['type_id']]
])
->first();
return $result;
}
Can the where statement be written in such a way that it does not need to be repeated?
You can factor out the common part of the code into a private method, then extend the base with the extra features for each scenario...
private function getBase () {
return $this->model->query()->where([
['able_type', $attributes['type']],
['able_id', $attributes['type_id']]
]);
}
public function all(array $attributes)
{
$rates = $this->getBase()
->get(['rate']);
return [
'count' => $rates->count(),
'average' => $rates->avg('rate')
];
}
public function show($attributes)
{
$result = $this->getBase()
->where('user_id', $attributes['user_id'])
->first();
return $result;
}
(although I am assuming this will work as I don't code Laravel).
public function get(array $attributes)
{
$rates = $this->model
->where('able_type', $attributes['type'])
->where('able_id', $attributes['type_id']);
if(!empty($attributes['user_id']))
{
return $rates->where('user_id', $attributes['user_id'])
->first();
}
return [
'count' => $rates->count(),
'average' => $rates->avg('rate')
];
}
Handle the both requests in a single function. The code is more neat & clean and easy to read & understand. Arrow function increase re readability over the arrays.
Laravel 5.8
PHP 7.4
I want to load the relationships conditionally like
http://127.0.0.1:8000/api/posts
and
http://127.0.0.1:8000/api/posts/1 are my end points now, I want to load comments like
http://127.0.0.1:8000/api/posts/?include=comments and
http://127.0.0.1:8000/api/posts/1/?include=comments
If the query parameter is there, only then it should load comments with posts or it should load only posts/post
I am doing this by referring a blog post
now, RequestQueryFilter
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
class RequestQueryFilter
{
public function attach($resource, Request $request = null)
{
$request = $request ?? request();
return tap($resource, function($resource) use($request) {
$this->getRequestIncludes($request)->each(function($include) use($resource) {
$resource->load($include);
});
});
}
protected function getRequestIncludes(Request $request)
{
// return collect(data_get($request->input(), 'include', [])); //single relationship
return collect(array_map('trim', explode(',', data_get($request->input(), 'include', [])))); //multiple relationships
}
}
and in helper
<?php
if ( ! function_exists('filter') ) {
function filter($attach)
{
return app('filter')->attach($attach);
}
}
?>
in PostController
public funciton show(Request $request, Post $post) {
return new PostResource(filter($post));
}
but when I am trying to retrieve
http://127.0.0.1:8000/api/posts/1/?include=comments getting no comments, with no error in log
A work around will be PostResource
public function toArray($request)
{
// return parent::toArray($request);
$data = [
'id' => $this->id,
'name' => $this->title,
'body' => $this->content,
];
$filter = $request->query->get('include', '');
if($filter){
$data[$filter] = $this->resource->$filter;
}
return $data;
}
I want to load the relationships conditionally like
Lazy Eager Loading using the load() call
The Lazy Eager Loading accomplishes the same end results as with() in Laravel, however, not automatically. For example:
?include=comments
// Get all posts.
$posts = Post::without('comments')->all();
if (request('include') == 'comments')) {
$posts->load('comments');
}
return PostResource::collection($posts);
Alternativelly, you could require the include query string to be an array:
?include[]=comments&include[]=tags
// Validate the names against a set of allowed names beforehand, so there's no error.
$posts = Post::without(request('includes'))->all();
foreach (request('includes') as $include) {
$posts->load($include);
}
return PostResource::collection($posts);
The call without() is only required in case you defined your model to automatically eager load the relationships you want to conditionally load.
With all data filtered in Controller, just make sure to display only loaded relations in your PostResource
public function toArray($request) {
$data = [...];
foreach ($this->relations as $name => $relation)
{
$data[$name] = $relation;
}
return $data;
}
I would create a custom resource for the posts with
php artisan make_resource
command.
E.g. PostResource.
The toArray function of the resource must return the data.
PostResource.php
public function toArray($request){
$data =['title' => $this->resource->title,
'body' => $this->resource->body,
'images' => new ImageCollection($this->whenLoaded('images')),
];
$filter = $request->query->get('filter', '');
if($filter){
$data['comments'] => new CommentCollection($this->resource->comments);
}
return $data;
}
Also, for collections, you need to create a ResourceCollection.
PostResourceCollection.php
class PostResourceCollection extends ResourceCollection
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request
* #return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}
In your controller:
PostsController.php
//show one post
public function show(Post $post, Request $request)
{
/**this response is for API or vue.js if you need to generate view, pass the resource to the view */
return $this->response->json( new PostResource($post));
}
//list of posts
public function index(Request $request)
{
$posts = Post::all();
/**this response is for API or vue.js if you need to generate view, pass the resource to the view */
return $this->response->json( new PostResourceCollection($posts));
}
Partial Solution
It will need a small change in resource class
public function toArray($request)
{
// return parent::toArray($request);
$data = [
'id' => $this->id,
'title' => $this->title,
'body' => $this->body,
'comments' => new CommentCollection($this->whenLoaded('comments')),
'images' => new ImageCollection($this->whenLoaded('images')),
];
return $data;
}
and it will load comments and images if loaded and that depends on the include query parameter, if that is not included, it will not load the relationship.
However,
In post collection
return [
'data' => $this->collection->transform(function($post){
return [
'id' => $post->id,
'title' => $post->title,
'body' => $post->body,
'comments' => new CommentCollection($post->whenLoaded('comments')),
'images' => new ImageCollection($post->whenLoaded('images')),
];
}),
];
will results in
"Call to undefined method App\Models\Customer::whenLoaded()",, if anyone suggests a complete solution, it will be a great help, if I will able to do, it I will update here.
I don't know what's wrong, but here's my script at my AppController.
function getData () {
$list_data = MyModel::all()->sortBy('id')->paginate(15);
$count_data = $list_siswa->count();
return view('pages.list', ['list' => $list_data, 'count' => $count_data]);
}
And here's my model
class MyModel extends Model {
protected $table = 'students';
protected $fillable = [
'id',
'name',
'class',
'gender',
'address'
];
}
Any idea? I think the problem is in my controller.
You must paginate a database query not a collection, therefore you must user orderBy instead of combining all with sortBy, I have tested the below code and can confirm it works
function getData () {
$list_data = MyModel::orderBy('id')->paginate(15);
$count_data = $list_data->count();
return view('pages.list', ['list' => $list_data, 'count' => $count_data]);
}
I want to write a dynamic update query in Laravel which accepts arguments and can be used in whole project.
Following is my Controller function:
public function editquery(Request $request)
{
$city_id = $request->input('city_id');
$city_name = $request->input('city_name');
$tbl = 'city';
$data = ['city_name'=>$city_name];
$wher = ('city_id',1);
General_model::editrecord($data,$wher,$tbl);
return redirect()->action('Admin_controller#cities_page')->with('status','Record Updated Successfully!');;
}
Below is my Model function:
public static function editrecord($data,$wher,$tbl)
{
return DB::table($tbl)->where($wher)->update($data);
}
The only problem here is that I cannot store the value ('city_id',1) in the $wher variable. This is the screenshot of the error:
link to the image file
Is there any other way to do this. Please Help.
The where method accepts an array of conditions.
$table = 'city';
$conditions = [
['city_id', '=', '1']
];
$data = ['city_name' => $city_name];
General_model::editRecord($table, $conditions, $data);
// In your model
public static function editRecord($table, $conditions, $data)
{
return DB::table($table)->where($conditions)->update($data);
}
You can also set multiple conditions.
$conditions = [
['city_id', '=', '1'],
['test', '=', 'test'],
];
Edit
This is the default where method
where($column, $operator = null, $value = null, $boolean = 'and')
Setting the fourth parameter to or will make the condition orWhere.
Example
$conditions = [
['city_id', '=', '1'],
['test', '=', 'test', 'or'],
];
You can't do this
public static function editrecord($data,$wher,$tbl)
{
return DB::table($tbl)->where($wher)->update($data);
}
Since, where is a function; it expects 2 or 3 arguments and not just 1 argument.
You will have to pass both the arguments like so
public static function editrecord($data, $where_column, $where_val, $tbl)
{
return DB::table($tbl)->where($where_column, $where_val)
->update($data);
}
Then, in your controller function
$where_column = 'city_id';
$where_val = 1;
General_model::editrecord($data,$where_column,$where_val,$tbl);
Your code is not exactly in the style of Laravel, why would you want to create a separate static function, if such tasks are easily solved by the standard features of Eloquent / Query Builder?
Eloquent example:
app/City.php
<?php
class City extends Model {
protected $table = 'city';
protected $primaryKey = 'city_id';
protected $fillable = ['city_name'];
}
In your controller:
City::findOrFail($city_id)->update([
'city_name' => $city_name
]);
Query Builder example:
DB::table('city')->where(['city_id' => $city_id])->update([
'city_name' => $city_name
]);
This is much easier to read, understand and support than functions that do similar things in an incomprehensible way.
Following Models:
class User extends Doctrine_Record {
public function setTableDefinition() {
$this->hasColumn ( 'iron', 'integer', 4 );
}
public function setUp() {
$this->hasMany ('Field as Fields', array(
'local' => 'id',
'foreign' => 'owner_id'
));
}
}
class Field extends Doctrine_Record {
public function setTableDefinition() {
$this->hasColumn('owner_id','integer',4);
$this->hasColumn('ressource_id','integer',4);
$this->hasColumn('ressource_amount','integer','2');
}
public function setUp() {
$this->hasOne('User as Owner',array(
'local' => 'owner_id',
'foreign' => 'id'
));
}
}
And I try following DQL:
$sqlRessourceUpdate = Doctrine_Query::create()
->update('Field f')
->set('f.Owner.iron','f.Owner.iron + f.ressource_amount')
->where('f.ressource_id = ?',1);
Result:
'Doctrine_Query_Exception' with message 'Unknown component alias f.Owner'
Basicly I just want to update the "iron" attribute from the Field-Owner according to the fields' value
I am guessing you can't reference other tables like that in your query.
This may not be the best way but, here is what I do
$q = Doctrine_Query::create()
->select('*')
->from('Field')
->where('ressource_id = ?',1); //btw resource has one 's'
$field = $q->fetchone();
$field->Owner['Iron'] += $field->ressource_amount;
$field->save();
EDIT:
Actually I don't know if that will work... this is more like what I do
$q = Doctrine_Query::create()
->select('*')
->from('Field')
->where('ressource_id = ?',1); //btw resource has one 's'
$field = $q->fetchone();
$user = $field->Owner;
$user['Iron'] += $field->ressource_amount; // I have never used a += like this, but in theory it will work.
$user->save();