I have a find() method in my if else statement that queries the database and returns the data as an array. The if part works fine. The problem is in the else part. When I try to access the index interface in the browser, am getting this error.
Unable to locate an object compatible with paginate.
RuntimeException
From what I have gathered so far, the paginate() method works with objects not arrays. Am stuck on how to come to my desired outcome. Am new to CakePHP, a not so advanced/complicated response would be appreciated. Thanks
/**
* Assets Controller
*
*
* #method \App\Model\Entity\Asset[] paginate($object = null, array $settings = [])
*/
class AssetsController extends AppController
{
/**
* Index method
*
* #return \Cake\Http\Response|void
*/
public function index()
{
$this->loadModel('Users');
$username = $this->request->session()->read('Auth.User.username');
$userdetail = $this->Users->find('all')->where(['username' => $username])->first();
$school = $userdetail->school_unit;
$roleid = $userdetail->role_id;
if ($roleid == 1) {
$this->paginate = [
'contain' => ['SchoolUnits', 'AssetConditions', 'AssetCategories', 'AssetGroups', 'AssetStatus']
];
$assets = $this->paginate($this->Assets);
$this->set(compact('assets'));
$this->set('_serialize', ['assets']);
} else {
$results = $this->Assets->find('all')->contain(['SchoolUnits', 'AssetConditions', 'AssetCategories', 'AssetGroups', 'AssetStatus'])->where(['school_unit_id' => $school])->first();
$assets = $this->paginate($this->$results);
$this->set(compact('assets'));
$this->set('_serialize', ['assets']);
}
}
Related
I'm trying to make reusable datatable instance
My Datatable Class :
class Datatables extends CI_Model {
protected $columnOrder;
protected $columnSearch;
protected $query;
public function __construct($columnOrder,$columnSearch,$query)
{
parent::__construct();
$this->columnOrder = $columnOrder;
$this->columnSearch = $columnSearch;
$this->query = $query;
}
/**
* Generate db query
*
* #return object
*/
private function getDatatablesQuery()
{
$i = 0;
foreach ($this->columnSearch as $item) {
if(#$_POST['search']['value']) {
if($i===0) {
$this->query->group_start();
$this->query->like($item, $_POST['search']['value']);
} else {
$this->query->or_like($item, $_POST['search']['value']);
}
if(count($this->columnSearch) - 1 == $i)
$this->query->group_end();
}
$i++;
}
if(isset($_POST['order'])) {
$this->query->order_by($this->columnOrder[$_POST['order']['0']['column']], $_POST['order']['0']['dir']);
} else if(isset($this->order)) {
$order = $this->order;
$$this->query->order_by(key($order), $order[key($order)]);
}
}
/**
* Generate db result
*
* #return integer
*/
public function getDatatables()
{
$this->getDatatablesQuery();
if(#$_POST['length'] != -1) $this->query->limit(#$_POST['length'], #$_POST['start']);
$query = $this->query->get();
return $query->result();
}
/**
* Count filtered rows
*
* #return integer
*/
public function countFiltered()
{
$query = $this->query->get();
return $query->num_rows;
}
/**
* Count all rows
*
* #return integer
*/
public function countAll()
{
return $this->query->count_all_results();
}
}
My FmrTable Class
<?php defined('BASEPATH') OR exit('No direct script access alowed');
require 'application/libraries/Datatables/Datatables.php';
class FmrTable {
protected $select;
protected $columnOrder;
protected $columnSearch;
protected $ci;
public function __construct()
{
$this->select = 'fmrs.id as id,sections.name as section,users.username as user,fmr_no,fmrs.status';
$this->columnOrder = ['id','section','user','fmr_no','status'];
$this->columnSearch = ['section','user','fmr_no','status'];
$this->ci = get_instance();
}
public function get()
{
$query = $this->ci->db
->select($this->select)
->from('fmrs')
->join('sections as sections', 'fmrs.section_id = sections.id', 'LEFT')
->join('users as users', 'fmrs.user_id = users.id', 'LEFT');
$query->where('section_id',$this->ci->session->userdata('section-fmr'));
}
$datatable = new Datatables($this->columnOrder,$this->columnSearch,$query);
return [
'list' => $datatable->getDatatables(),
'countAll' => $datatable->countAll(),
'countFiltered' => $datatable->countFiltered()
];
}
}
This always throw a database error that says Error Number: 1096 No tables used
This came from the countFiltered() method, when i tried to dump the $query without get(), it returned the correct object instance but if i do this then the num_rows property will never available, but when i add the get() method, it will return the 1096 error number
How to solve this ?
A call to ->get() resets the query builder. So when you call ->get() for the second time (in countFiltered) the table name and the remainder of the query have been cleared and that's why you get the error.
Solution is to use query builder caching. This allows you to cache part of the query (between start_cache and stop_cache) and execute it multiple times: https://www.codeigniter.com/userguide3/database/query_builder.html?highlight=start_cache#query-builder-caching
Use flush_cache to clear the cache afterwards, so the cached query part does not interfere with subsequent queries in the same request:
FmrTable
public function get()
{
$this->ci->db->start_cache();
$query = $this->ci->db
->select($this->select)
->from('fmrs')
->join('sections as sections', 'fmrs.section_id = sections.id', 'LEFT')
->join('users as users', 'fmrs.user_id = users.id', 'LEFT');
$query->where('section_id',$this->ci->session->userdata('section-fmr'));
//}
$this->ci->db->stop_cache();
$datatable = new Datatables($this->columnOrder,$this->columnSearch,$query);
$result = [
'list' => $datatable->getDatatables(),
'countAll' => $datatable->countAll(),
'countFiltered' => $datatable->countFiltered()
];
$this->ci->db->flush_cache();
return $result;
}
And probably use num_rows() instead of num_rows here, num_rows gave me a NULL instead of a count:
Datatables
/**
* Count filtered rows
*
* #return integer
*/
public function countFiltered()
{
$query = $this->query->get();
return $query->num_rows();
}
This one has happened to me before but I have no idea why and how to avoid it. So I have a static function in a Model which gets all the database rows and uses a foreach loop to read another table but I am unable to correctly read the row data:
public static function test()
{
$accounts = self::where( 'is_enabled', 1 )->get();
foreach ( $accounts as $account ) {
$map = AccountMap::where( 'account_id', $account->id )->first();
$location = Location::getLocation( $map->location_id );
$data = $location->getData();
}
}
So the above function gathers an array of items ($accounts) this is then passed into a foreach loop all is fine to this point but if i now use $account->id it is null. The id is shown in the Account object in its attributes folder.
A very similar function is used elsewhere in this model but it uses a passed id and this one works (however $account->id is null). The issue is not the database or column names:
public static function getThisLocation( $id )
{
$account = self::find( $id );
$map = AccountMap::where( 'account_id', $id )->first();
location = Location::getLocation( $map->location_id );
$data = $location->getData();
return $data;
}
*** EDIT ***
Account, AccountMap and Location are all Eloquent models
namespace App\Models;
use Eloquent;
use App\Notifications\AccountMessages;
use Kyslik\ColumnSortable\Sortable;
use Illuminate\Notifications\Notifiable;
/**
* #method static find(int $id)
*/
class Account extends Eloquent
{
use Sortable;
use Notifiable;
public $sortable = [
'id',
'name',
'lastupdate',
'url'
];
public static function test()
{
$accounts = self::where( 'is_enabled', 1 )->get();
foreach ( $accounts as $account ) {
$map = AccountMap::where( 'account_id', $account->id )->first();
$location = Location::getLocation( $map->location_id );
$data = $location->getData();
}
}
public static function getThisLocation( $id )
{
$account = self::find( $id );
$map = AccountMap::where( 'account_id', $id )->first();
location = Location::getLocation( $map->location_id );
$data = $location->getData();
return $data;
}
}
namespace App\Models;
use Eloquent;
use Kyslik\ColumnSortable\Sortable;
/**
* #method static where(string $string, int $id)
*/
class AccountMap extends Eloquent
{
use Sortable;
public $sortable = [
'id',
'account_id',
'location'
];
}
*** MORE EDIT ***
I have confirmed that using $account->attributes['id'] has worked but I've no idea why what I expected to work didn't ($account->id)
The problem must be something related to communication of your model and migration.
Add this dd() to your current test function:
public static function test()
{
$accounts = self::where( 'is_enabled', 1 )->get();
foreach ( $accounts as $account ) {
if ($account->id){
$map = AccountMap::where( 'account_id', $account->id )->first();
$location = Location::getLocation( $map->location_id );
$data = $location->getData();
} else {
dd($account)
}
}
}
Then Check the result and see is there the id filed on your response? If not, The id field doesn't exist on your self Model and it's Your problem's cause.
Finally, Check your model fields easily with :
public function testReturnOfSelfModel()
{
$data= self::all();
dd($data);
}
If you have id on this dd function, Your Model working properly. If not, you dont have id field.
Also, it is more professional to change Capitalize your model's first charachter. It sholud be Self, not self.
I'd suggest to setup two proper data Model (a migration would need to create these tables):
class Account extends Model {
protected $table = 'accounts';
public $timestamps = false;
/**
* The attributes that are mass assignable.
* #var array
*/
protected $fillable = [];
/**
* The attributes that should be hidden for arrays.
* #var array
*/
protected $hidden = [];
}
Unless defining protected $table it will definitely not know what to do.
It's rather unclear what you're even trying to accomplish with AcountMap, but it may need a relation defined; which eg. with return $this->belongsTo(Account::class); ...while simply adding lat & lng to class Account would be far less complex and perfectly fine, while it's only 1 location.
working with Laravel PHP, I have this model with a constructor where i set the attributes:
class NutritionalPlanRow extends Model
{
use HasFactory;
private $nutritional_plan_id;
private $aliment_id;
private $nomeAlimento;
public function __construct($plan = null,
$aliment = null,
array $attributes = array()) {
parent::__construct($attributes);
if($plan){
$this->nutritional_plan()->associate($plan);
$this->nutritional_plan_id = $plan->id;
}
if($aliment){
$this->aliment()->associate($aliment);
$this->aliment_id = $aliment->id;
$this->nomeAlimento = $aliment->nome;
}
}
/**
* Get the plan that owns the row.
*/
public function nutritional_plan()
{
return $this->belongsTo('App\Models\NutritionalPlan');
}
/**
* Get the aliment record associated with the NutritionalPlanRow.
*/
public function aliment()
{
return $this->belongsTo('App\Models\Aliment');
}
/**
* The attributes that aren't mass assignable.
*
* #var array
*/
protected $guarded = [];
/**
* Get the value of nomeAlimento
*/
public function getNomeAlimentoAttribute()
{
return $this->nomeAlimento;
}
/**
* Get the value of plan_id
*/
public function getNutritional_Plan_IdAttribute()
{
return $this->nutritional_plan_id;
}
/**
* Get the value of aliment_id
*/
public function getAliment_IdAttribute()
{
return $this->aliment_id;
}
}
Then I have a controller where I initialize the object:
public function addAlimentToPlan(Request $request){
$planId = $request->planId;
$alimentId = $request->alimentId;
$validatedData = Validator::make($request->all(), [
'planId' => ['required'],
'alimentId' => ['required'],
]);
if ($validatedData->fails()) {
return back()->withErrors($validatedData, 'aliment');
}
$plan = NutritionalPlan::find($planId);
$aliment = Aliment::find($alimentId);
$nutritionalPlanRow = new NutritionalPlanRow($plan, $aliment);
Log::info('Nome Alimento '.$nutritionalPlanRow->getNomeAlimentoAttribute());
$nutritionalPlanRow->save(); //
Toastr::success( 'Alimento aggiunto', '',
["positionClass" => "toast-bottom-right",
"closeButton" => "true"]);
return back();
}
The save operation return this error:
SQLSTATE[23502]: Not null violation: 7 ERRORE: null value in column "nomeAlimento" of relation "nutritional_plan_rows"
but logging the $nutritionalPlanRow->getNomeAlimentoAttribute() the attribure is enhanced.
Someone can help me?
Thank you.
In your constructor you have the following line:
$this->nomeAlimento = $aliment->nome;
You believe that this will fill the attribute in the eloquent model, but that is not happening. Normally such an assignment will pass the magic __set method on the model, but not during model/object construction.
You actually assign it to a property on the object, which is later accessible by your log function, but eloquent doesn't know about it. Therefore it is not sent to the database, resulting in a null error (no default value).
You may use the following to set the values in the constructor:
$this->setAttribute('nomeAlimento', $aliment->nome);
This calls the setAttribute function on the eloquent model, the attribute this becomes part of the model.
(Make sure to change also the other line in your constructor where you assign a value to the object)
When I run the code I get no error but the data I am trying to display is not displaying it's just blank.. can someone tell me what I'm doing wrong?
My controller:
public function openingPage($id) {
$this->getGames();
$games = $this->getGames();
return view('caseopener')->with('games',$games);
}
private function getGames() {
$games = array();
foreach ($this->data->items as $item) {
$game = new Game($item);
$games[] = array(
'id' => $game['id'],
'name' => $game['name'],
'price' => $game['price'],
'image' => $game['image'],
);
}
return $games;
}
The 'Game' Model that is used in 'getGames function':
class Game extends Model
{
private $id;
public $data;
public function __construct($id) {
parent::__construct();
$this->id = $id;
$this->data = $this->getData();
}
private function getData() {
$game = DB::table('products')->where('id', 1)->first();
if(empty($game)) return array();
return $game;
}
}
The view:
#foreach ($games as $game)
<div class="gold">$ {{ $game['price'] }}</div>
#endforeach
I think you are over-complicating things. You could simplify your flow like this:
Given your provided code, it seems like you are using a custom table name ('products') in your Game model. So we'll address this first:
Game.php
class Game extends Model
{
protected $table = 'products'; //
}
Now, it seems like you're searching an array of Game ids ($this->data->items). If so, you could make use of Eloquent for your query, specially the whereIn() method:
YourController.php
public function openingPage($id)
{
$games = Game::whereIn('id', $this->data->items)->get();
return view('caseopener')->with('games', $games);
}
Optionally, if you want to make sure of just returning the id, name, price and image of each Game/product, you could format the response with API Resources:
php artisan make:resource GameResource
Then in your newly created class:
app/Http/Resources/GameResource.php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class GameResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'price' => $this->price,
'image' => $this->image,
];
}
}
So now just update your controller:
YourController.php
use App\Http\Resources\GameResource;
public function openingPage($id)
{
$games = Game::whereIn('id', $this->data->items)->get();
return view('caseopener')->with('games', GameResource::collection($games));
} // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I am using fractal (fractal.thephpleague.com) to develop an API with Laravel (laravel.com). It is an amazing library, by the way.
In certain web service, I need to return information of several nested models, which have 3 levels deep. That is, I have a Survey model which has many Survey Items, and each one of them has, in turn, many Survey Item Results (each one of a user). Well, I need the data from all of them, classified, that is:
"surveys": [
{
"id": 1,
...,
"items": [
{
"id": 14,
...,
"results": [
{
"id": 45,
...
},
{
...
}
]
},
{
...
}
]
},
{
...
}
}
With transformers and includes, I get the surveys and survey items info without problems, but I also need the survey item results...
That is, I need something like 2-level "nested" includes, to get the information of the third level.
My best approach, so far (only returning two levels: surveys and survey items). In my controller:
return fractal() -> transform(
Survey::where(...),
new SurveyTransformer()
) -> include(['SurveyItems']) -> respond();
Any help is much appreciated.
Thanks in advance.
Here's what I normally do
Survey Transformer
<?php
namespace App\Transformers;
use League\Fractal;
use App\Survey;
class SurveyTransformer extends Fractal\TransformerAbstract
{
/**
* List of resources possible to include
*
* #var array
*/
protected $availableIncludes = [
'items'
];
public function transform(Survey $survey)
{
return [
'id' => (int) $user->id,
];
}
/**
* Include Items
*
* #param App\Survey $survey
* #return League\Fractal\CollectionResource
*/
public function includeItems(Survey $survey)
{
$items = $survey->items;
if (!is_null($items)) {
return $this->collection($items, new ItemTransformer);
}
return;
}
}
Item Transformer
<?php
namespace App\Transformers;
use League\Fractal;
use App\Item;
class ItemTransformer extends Fractal\TransformerAbstract
{
/**
* List of resources possible to include
*
* #var array
*/
protected $availableIncludes = [
'results'
];
public function transform(Item $item)
{
return [
'id' => (int) $user->id,
];
}
/**
* Include results
*
* #param App\Item $item
* #return League\Fractal\CollectionResource
*/
public function includeResults(Item $item)
{
$results = $item->results;
if (!is_null($results)) {
return $this->collection($results, new ResultTransformer);
}
return;
}
}
On my base controller
/**
* get fractal tranformed data
* #param $resource
*/
protected function fractalResponse($resource, array $includes = [])
{
$manager = new Manager();
$manager->setSerializer(new DataArraySerializer()); //or what ever you like
if (sizeof($includes) == 0) {
$data = $manager->createData($resource)
->toArray();
} else {
$manager->parseIncludes($includes);
$data = $manager->createData($resource)
->toArray();
}
return $data;
}
then
$resource = new \League\Fractal\Resource\Collection($survies, new SurveyTransformer);
$response_data = $this->fractalResponse($resource, ['items.results'])