Custom Query in Cakephp 3.0? - php

I want to make a custom query in Cakephp. I've been reading this: Query Builder
The problem that every example are like:
$query = $articles->find()
->where([
'author_id' => 3,
'OR' => [['view_count' => 2], ['view_count' => 3]],
]);
But in my PostController I have this:
public function view($id = null)
{
$post = $this->Posts->get($id, [
'contain' => ['Users']
]);
$this->set('post', $post);
$this->set('_serialize', ['post']);
}
I don't have anything with find(). And I don't know what is doing the last part of code
This is the query that I want to use:
public function index()
{
$query = $posts->find()->where([
'userfk' => 1
]);
}
But it isn't working, I don't know how to display the query result.
How can I have the code to working right?
Thanks!

If you want use Query Builder
http://book.cakephp.org/3.0/en/orm/query-builder.html
PostsController.php
public function index() {
$posts = $this -> Posts -> find() -> where(['userfk' => 1]);
$this -> set('post', $posts);
}
By the way, if the result get few $posts
Your set should be $this -> set('posts', $posts); and your view have
<?php foreach($posts as $post): ?>
<!-- your code -->
<?php endforeach; ?>
///////
EDIT
Adding Paginator option
PostsController.php
public function index() {
$this -> paginate['contain'] = ['Users'];
$this -> paginate['conditions'] = ['Posts.userfk' => 1];
$this -> set('posts' , $this -> paginate($this -> Posts));
}

public function index()
{
$query = $posts->find()->where([
'userfk' => 1
]);
//or you can use dynamic finders
$query = $posts->findByUserfk(1);
$this->set('post', $query);
}
in your view. print all result;
pr($post);
or you can use foreach to iterate the result
foreach($post as $i => $val) {
echo $val->userfk;
}

Load Model before use in function
Please use in controller
public function initialize() {
parent::initialize();
$this->loadModel('Posts');
}
Then Index function:-
public function index()
{
$posts = $this->Posts->find('all', ['conditions' => ['userfk' => 1]]);
$this->set('posts',$posts);
//pr($posts->toArray());// Print Posts array
}

I do not understand why do you pass $pots in( $query = $posts->find()).
public function index()
{
$posts = $this->Post->find('all', ['conditions' => ['userfk' => 1]]);
$this->set('post', $posts );
//print_r($posts);you can print array.
}

Related

Exception in laravel 5.8 : Trying to get property of non-object when trying to edit data

Recently I've started with Laravel 5.8 and I'm trying to make the edit button which will update the row from the database. and when the edit button click, Laravel raise an error like this
this is my Controller:
public function edit($id_book){
$book = Books::find($id_book);
return view('/books',['book'->$book]);
}
public function UploadEdit(Request $request){
DB::table('books')->where('id_book',$request->id_book)->update([
'judul' => $request->judul,
'gambar' => $request->gambar,
'kategori' => $request->kategori,
'deskripsi' => $request->deskripsi,
'ketersediaan' => $request->ketersediaan,
'lokasi' => $request->lokasi
]);
return redirect()->back();
}
this is my models:
class Books extends Model
{
protected $table = "books";
protected $primaryKey = "id_book";
protected $fillable = [
'judul',
'gambar',
'kategori',
'deskripsi',
'ketersediaan',
'lokasi'
];
}
I was trying to solve this problem with change edit function in the controller:
from this
public function edit($id_book){
$book = Books::find($id_book);
return view('/books',['book'->$book]);
}
to this
public function edit($id_book){
$book = Books::find($id_book);
return view('/books',compact('book'));
}
and this:
public function edit($id_book){
$book = Books::find($id_book);
return view('/books')->withBooks('$book');
}
but it doesn't work
I think error shows id_book is wrong.
You should use findOrFail() method.
public function edit($id_book){
$book = Books::findOrFail($id_book);
return view('/books',compact('book'));
}
Change your line 58 (edit() function's return statement) to:
return view('/books',compact('book'));
It seems you used -> instead => to assign value
Change
return view('/books',['book'->$book]);
to
return view('/books',['book' => $book]);
Try this
public function edit($id_book) {
$book = Book::find($id_book);
return view('books.edit', compact('book'));
}
Try this:
public function edit($id_book){
$book = Books::where('id_book', $id_book)->first();
return view('/books', compact('book'));
}

Adding filter to eloquent resource to attach relationship conditionally

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.

what's wrong with my foreach in laravel?

I'm going to pass data in array
but here's the code
public function test($pid){
$row_for_menu = Menu::where('parent_id', '=', $pid)->get();
$menu = array();
foreach ($row_for_menu as $menu_one_by_one){
$menu[] = array('title' => $menu_one_by_one->name, 'nodes' => $this->test($menu_one_by_one->id));
}
dd($menu);
}
public function index(Request $request)
{
$this->test(1);
//$perPage = 25;
//$menu = Menu::paginate($perPage);
//return view('admin.menu.index', compact('menu'));
}
my foreach in first round worked well
but in the end responsed [ dd($menu) ] empty array and error 500 !
my english is not well sorry ;)
try this demo working fine
public function test($pid){
$row_for_menu = Menu::where('parent_id', '=', $pid)->get()->lists('id','name');
$menu = array();
//foreach ($row_for_menu as $menu_one_by_one){
//$menu[] = array('title' => $menu_one_by_one->name, 'nodes' => $this->test($menu_one_by_one->id));
//}
dd($menu);
}
$menu is empty ie. array() , because $row_for_menu is empty
public function test($pid){
$row_for_menu = Menu::where('parent_id', '=', $pid)->get();
$menu = array();
foreach ($row_for_menu as $menu_one_by_one){
$menu[] = array('title' => $menu_one_by_one->name, 'nodes' => $this->test($menu_one_by_one->id));
}
if(count($menu)){
dd($menu);
}
}
public function index(Request $request)
{
$this->test(1);
//$perPage = 25;
//$menu = Menu::paginate($perPage);
//return view('admin.menu.index', compact('menu'));
}
You should define a relation between your menu items on the model instead of recursively looping to build the menu.
// Menu model
public function parent ()
{
return $this->belongsTo(__CLASS__, 'parent_id');
}
public function children ()
{
return $this->hasMany(__CLASS__);
}
Then in your view:
#foreach ($menu->children as $child)
// $child->name
// $child->link
#endoreach
Your controller would simply be:
// assuming you pass a menu id for specific page or similar
public function index(Request $request) {
$menu = Menu::find($request->getAttribute('menu_id'))->toArray();
return view('admin.menu.index', compact('menu'));
}
the test function must return $menu after foreach like this
public function test($pid){
$menus = Menu::where('parent_id', '=', $pid)->get();
$menu = array();
foreach ($menus as $menu_one_by_one){
$menu[] = array('text' => $menu_one_by_one->name, 'nodes' => $this->test($menu_one_by_one->id));
}
return $menu;
}
public function index(Request $request)
{
$x = $this->test(1);
dd($menu)

How to get flexibility in laravel sql query

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.

pass variable between functions in same controller

class AppointmentsController extends AppController {
public function view($id = null) {
if (!$this->Appointment->exists($id)) {
throw new NotFoundException(__('Invalid appointment'));
}
$options = array('conditions' => array('Appointment.' . $this->Appointment->primaryKey => $id));
$this->set('appointment', $this->Appointment->find('first', $options));
$kk = $this->Appointment->find('first', array('fields' => 'status', 'conditions' => array('Appointment.id' => $id)));
$ss = reset($kk);
$stats = reset($ss);
}
}
I have set $stats via getting value from DB and i want to use in in another function in same controller
then I want to use like this
class AppointmentsController extends AppController {
function confirm(){
$stats = 'New';
}
}
Seeing that it's the same class, have you tried using $this->stats instead of a local variable?
You can call another function in your first function like
$this->confirm($status); // calling confirm function
function confirm($status)
{
//use status as you want
}
Or you can set status as global variable then this can you accessible in any function.
I think, you've to create function in the model that will return the value of the field by id.
<?php
// model/AppModel.php
public function getFieldById($field='id', $id){
return $this->field($field, array('id' => $id));
}
// in controller function you can access this like.
$status = $this->Appointment->getFieldById('status', $id); // $id will be appointment id.
?>

Categories