cakephp3- table sorting not working - sorting

Version: 3.3.*
I’m using the $paginator->sort() method to create links in the column headers for tables of paginated search results in my CMS. You should be able to click them once to sort in ascending order, and then click again to reverse into descending order. But the reverse was never working for me.
But this is not for all fields. Let say I have 5 fields.
<tr class="design">
<th scope="col"><?= $this->Paginator->sort('Students.id','Id',array('class'=>'link')); ?></th>
<th scope="col"><?php echo $this->Paginator->sort('Students.userName','Name',array('class'=>'link')); ?></th>
<th scope="col"><?php echo $this->Paginator->sort('Students.age','Age',array('class'=>'link')); ?></th>
<th scope="col"><?php echo $this->Paginator->sort('Students.currentClass','Class',array('class'=>'link')); ?></th>
<th scope="col"><?php echo $this->Paginator->sort('Students.dateJoined','Joined Date',array('class'=>'link')); ?></th>
</tr>
I can sort the table both ways using username, age, and school but not using id and joined. When I fetch the list initially I have specified in my Model function to fetch result based on id and joined in ascending order.
Is it because of this, I'm not being able to sort it in descending order?
Is there any way I can achieve this?
Controller function
public function index()
{
//Listing Students
$this->paginate = [
'finder' => 'ListStudents',
'sortWhitelist' => ['Students.id',
'Students.userName',
'Students.age',
'Students.currentClass',
'Students.dateJoined',],
'limit' => 25,
];
$students = $this->paginate($this->Students);
$this->set(compact('students'));
$this->set('_serialize', ['students']);
}
Model Function
public function findListStudents(Query $query, array $options)
{
$query
->select(['id','userName','age','currentClass','dateJoined'
])
$query->group('Students.id');
$query->order(['status' => 'asc']);
$query->order(['Students.dateJoined' => 'asc']);
$query->order(['Students.id' => 'asc']);
return $query;
}
The issue mentioned in this link is similar to the one I'm facing.I tried the solution mentioned in it but it ain't working.

To achieve this, you have to modify your controller and model functions a little bit.
First of all, add extraOptions to your controller function
public function index()
{
//Listing Students
$this->paginate = [
'finder' => 'ListStudents',
'sortWhitelist' => ['Students.id',
'Students.userName',
'Students.age',
'Students.currentClass',
'Students.dateJoined',],
'limit' => 25,
'extraOptions' =>[
'params' => $params ]
];
$students = $this->paginate($this->Students);
$this->set(compact('students'));
$this->set('_serialize', ['students']);
}
Now you need to override your dateJoined and id sort order. By default, its asc.
If user passes a specific direction, it should be taken. So, for that you need to modify your model function a little.
public function findListStudents(Query $query, array $options)
{
$query
->select(['id','userName','age','currentClass','dateJoined'
])
$query->group('Students.id');
$direction = (!empty($options['extraOptions']['params']['direction'])) ? $options['extraOptions']['params']['direction'] : 'asc';
$query->order(['status' => 'asc']);
$query->order(['Students.dateJoined' => $direction]);
$query->order(['Students.id' => $direction]);
return $query;
}
Now sorting will work fine when you are passing a specific direction.

When I fetch the list initially I have specified in my Model function
to fetch result based on id and joined in ascending order. Is it
because of this, I'm not being able to sort it in descending order?
Yes, this is the case. You can verify that by commenting out the $query->order statements in your custom finder method. You will find out that descentant sorting Students.id will work then.
I would recommend to simply leave the sorting out of the custom finder method and define the default sorting options when configuring your $paginate variable instead.
StudentsController
public function index()
{
$this->paginate = [
'finder' => 'ListStudents',
'sortWhitelist' => [
'Students.id',
'Students.userName',
'Students.age',
'Students.currentClass',
'Students.dateJoined'
],
'limit' => 25,
// add custom sorting options here
'order' => [
'Students.status' => 'asc',
'Students.dateJoined' => 'asc',
'Students.id' => 'asc'
]
];
$students = $this->paginate($this->Students);
$this->set(compact('students'));
$this->set('_serialize', ['students']);
}
StudentsTable
public function findListStudents(Query $query, array $options)
{
$query ->select([
'id', 'userName', 'age', 'currentClass', 'dateJoined'
]);
$query->group('Students.id');
// remove the sorting here
/*
$query->order(['status' => 'asc']);
$query->order(['Students.dateJoined' => 'asc']);
$query->order(['Students.id' => 'asc']);
*/
return $query;
}

Related

Best Way to Count Record Many Table - Laravel

I want to display the number of records from several tables at once in one view.
I've tried it using eloquent count.
public function index(){
$order = Order::count();
$owner = Owner::count();
$room = Room::count();
$member = Transaction::where([
['status', 'waiting'],
['type', 1]
])->count();
$highlight = Transaction::where([
['status', 'waiting'],
['type', 2]
])->count();
return view('admin.index', [
'order' => $order,
'owner' => $owner,
'room' => $room,
'member' => $member,
'highlight' => $highlight
]);
}
Is there a better way?
You could also use view-composers. But this is not better, just different. I mean you have to query anyway, so why do you think there should be a better way?

Why stored duplicate rows to table

I have custom query for adding parsed remote posts to table:
foreach ($parsedPosts as $post) {
$hasRemotePostAlready = Post::where('remote_post_id', $post->id)->first();
if(null === $hasRemotePostAlready) {
$data = [
'title' => $post->title,
'description' => $post->description,
'remote_post_id' => $post->id
];
Post::create($data);
}
}
Variable $parsedPosts has more then 3500 posts and when I run my script to add posts then any remote posts duplicated. Why they posts duplicated and why not work my condition:
$hasRemotePostAlready = Post::where('remote_post_id', $post->id)->first();
How I can fix duplicating rows problem in my case?
You can use firstOrCreate or updateOrCreate methods for this case, take a look at the documentation
In your case try this:
$hasRemotePostAlready = Post::firstOrCreate(
[
'remote_post_id' => $post->id // columns to check if record exists
],
[
'title' => $post->title,
'description' => $post->description
]
);

Laravel - Iterating pivot data (Property does not exist on this collection instance)

I have a many-to-many relationship between Order and Product
Product.php
public function orders()
{
return $this->belongsToMany(Order::class)
->withTimestamps()
->withPivot('qty');
}
Order.php
public function products()
{
return $this->belongsToMany(Product::class)
->withTimestamps()
->withPivot('qty');
}
Now whenever I try to utilize an iteration in a view (in this case I am just trying to show the form which iterates through all available Products, I always receive the error...
Property [products] does not exist on this collection instance.
create.blade.php
#foreach ($products->orders as $product)
# Order inputs are here
{{ Form::text('qty', $product->pivot->qty, [
'type' => 'tel',
'min' => 0,
'max' => 20,
'step' => 1,
'placeholder' => '0',
'class' => 'meal'
]) }}
#endforeach
I have also attempted #foreach ($products->orders as $product) and both approaches give me that same error.
I have attempted many different ways in my Controller to fix this error, here is my last attempt:
OrderControlller.php
public function create()
{
$user = Auth::user();
$products = Product::get();
$orders = $user->orders;
return view('orders.create', compact('products', 'orders', 'user'));
}
UPDATE
#alan's answer is correct, I am sure, however...
I am still getting "Property [pivot] does not exist on this collection instance" whenever I try to run an iteration.
The concept of an iteration inside of an iteration in this instance is confusing for me.
I cannot visualize how Laravel is handling the pivot connection. In tinker when I load up just the Product table, there is no qty column. (This makes sense because that is on the pivot table). This also explains this new error.
Should I be doing something in the vein of this? :
changed create.blade.php
#foreach ($products as $product)
{{ Form::text('qty', $product->orders->pivot->qty }}
OrderController.php
$user = Auth::user();
$orders = $user->orders;
$products= []; #pass products to view as an array
$p = $orders->products; #this relationship brings in pivot data?
foreach ($p as $orders) {
#would I then somehow pass just this qty here?
}
Problem is I am always getting a "Property does not exist" error, be it with 'products', 'orders', or 'pivot'.
This should work. You were trying to access the orders property on the $products variable, which is a Laravel Collection (get method on model returns a collection). So instead of doing that you just iterate through the products and access the pivot table from the individual product model.
#foreach ($products as $product)
# Order inputs are here
{{ Form::text('qty', $product->orders->pivot->qty, [
'type' => 'tel',
'min' => 0,
'max' => 20,
'step' => 1,
'placeholder' => '0',
'class' => 'meal'
]) }}
#endforeach
Update:
Actually that makes sense. A record on the pivot table defines an association between an order and a product. So for you to access a record in the pivot table you must access the product or order from its relationship. This is what I would do.
OrderController.php
$user = Auth::user();
$user->load("orders.products"); // eager load relationship to avoid N+1 problem
$orders = $user->orders;
return view('orders.create', compact('orders', 'user'));
create.blade.php
#foreach ($orders as $order)
#foreach ($order->products as $product)
{{ Form::text('qty', $product->pivot->qty, [
'type' => 'tel',
'min' => 0,
'max' => 20,
'step' => 1,
'placeholder' => '0',
'class' => 'meal'
]) }}
#endforeach
#endforeach
Some resources:
Eager loading
Many to Many relations

Cakephp 3 Dynamic limit parameter when using contain

CakePHP Version 3.5.5
My end goal is to provide the user the functionality to change the amount of results displayed via a select list on the index view. Also I need the initial page load to be sorted by area_name asc.
// WHAT I'VE DONE
I changed where I was stipulated the limit parameter which can be seen below.
// AREAS CONTROLLER
public $paginate = [
'sortWhitelist' => [
'Areas.area_name', 'Users.first_name', 'Users.last_name'
]
//'limit' => 1, // REMOVED FROM HERE
//'order' => [ // REMOVED FROM HERE
//'Areas.area_name' => 'asc'
//]
];
public function index()
{
$query = $this->Areas->find('all')
->contain([
'Users'
])
->where(['Areas.status' => 1]);
$limit = 1;
$this->paginate = [
'order' => ['Areas.area_name' => 'asc'], // ADDED HERE
'limit' => $limit // ADDED HERE
];
$this->set('areas', $this->paginate($query));
}
And I declare the pagination sort links like:
// AREAS INDEX VIEW
<?= $this->Paginator->sort('Areas.area_name', __('Area Name')) ?>
<?= $this->Paginator->sort('Users.first_name', __('First Name')) ?>
<?= $this->Paginator->sort('Users.last_name', __('Last Name')) ?>
// RESULT
The above code works on all index methods within the application that don't use contain but when I implemented this solution here everything worked except I cannot sort on the associated data - IE: Users first and last name?
=========================================================================
WHAT I'VE TRIED
// Attempt 1
I added an initialize method above the public $paginate class like:
public function initialize()
{
$limit = 1;
}
public $paginate = [
'sortWhitelist' => [
'Areas.area_name', 'Users.first_name', 'Users.last_name'
]
'limit' => $limit,
'order' => [
'Areas.area_name' => 'asc'
]
];
public function index()
{
$query = $this->Areas->find('all')
->contain([
'Users'
])
->where(['Areas.status' => 1]);
$this->set('areas', $this->paginate($query));
}
And the view I left the same.
// Result for Attempt 1
syntax error, unexpected ''limit'' (T_CONSTANT_ENCAPSED_STRING), expecting ']' on line 36 which is 'limit' => $limit,
=========================================================================
// Attempt 2
I tried to add the limit parameter and order array to the query like:
public function index()
{
$limit = 1;
$query = $this->Areas->find('all')
->contain([
'Users'
])
->where(['Areas.status' => 1])
->order(['Areas.area_name' => 'asc'])
->limit($limit);
$this->set('areas', $this->paginate($query));
}
// Result for Attempt 2
The result set was not ordered by the area_name and not limited to 1 result.
=========================================================================
// Attempt 3
I then changed the query and tried the following just to see if I could get a dynamic limit working:
$limit = 1;
$query = $this->Areas->find('all')
->contain('Users', function ($q) {
return $q
//->order('Areas.area_name' => 'asc'),
->limit($limit);
})
->where(['Areas.status' => 1]);
$this->set('areas', $this->paginate($query));
// Result for Attempt 3
The result set was not limited to 1 result.
=========================================================================
ADDITIONAL INFORMATION
// USERS TABLE
$this->hasOne('Areas', [
'foreignKey' => 'user_id'
]);
// AREAS TABLE
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
I searched through the following cookbook sections (Pagination, Query Builder, Retrieving Data & Result Sets and Associations - Linking Tables Together) but I can't find a way to get this working so any help would be much appreciated.
Many thanks. Z.
You are overwriting the $paginate property in your index() method, so your settings including the whitelist are being lost.
Set the keys directly instead:
$this->paginate['order'] = ['Areas.area_name' => 'asc'];
$this->paginate['limit'] = $limit;

cant get ESelect2 extension in yii concatenate two or more columns in the dropdown

Eselect2 is the extension am using and myADropDown() fetches the data and displays but then my need is concatenate two or more columns in the dropdown
model1 view
<?php $this->widget('ext.select2.ESelect2', array(
'name' => 'id',
'model' => $model,
'options' => array(
'placeholder' => $model->getAttributeLabel('id'),
),
'data' => $model->myADropDown(),
));?>
model1
public function getConcatened()
{
return $this->name.' '.$this->locate.' '.$this->rating;
}
public function myADropDown()
{
$vid=Yii::app()->SESSION['vid'];
$gid=Model2::model()->xyz($vid);
$list=CHtml::listData($gid,'id','concatened');
return $list;
}
// id is fk in another tbl
in the dropdownlist my need is name ,location,rating for each person , am unable to do so
Please Let me know how do i achieve it
For concatenating the table fields in listdata, Please check this Yii forum answer:
Link
As per the link suggestion, you need to add a function in model to get concatenated string.
public function __get($key){
$action = 'get'.str_replace(' ','',ucwords(str_replace('_',' ',$key)));
if(method_exists($this, $action)) return $this->$action();
return parent::__get($key);
}

Resources