I have two domain models:
class Resource{
String name
static mapping = {
sort name:"asc"
}
}
class ResourceGroup{
String groupName
static hasMany = [resources: Resource]
}
controller:
def resGroups = ResourceGroup.findAll()
render (
view: "index",
model: [resourcegroups: resGroups]
)
so and now in my gsp:
<g:each in="${resourcegroups}" var="item" status="i">
...
<g:each in="${item.resources}" var="res" status="y">
<!-- THESE ITEM.RESOURCES ARE UNSORTED! -->
</g:each>
...
</g:each>
my Question is how can I sort this "item.resources"? this is a persistent set of hibernate! I thought this could be handled with the mapping sort name: 'asc', but it doesn't work :-(
try item.resources.sort { it.name } for "asc",
or item.resources.sort { it.name }.reverse() for "desc".
You cannot have a default sort on a one-to-many or many-to-many relationship. See documentation here, paying particular attention to the note at the bottom that says:
These mappings will not work for default unidirectional one-to-many or many-to-many relationships because they involve a join table. See this issue for more details. Consider using a SortedSet or queries with sort parameters to fetch the data you need.
The default sort you have specified actually does sort a list of Resource objects (i.e. if you got the list like this Resource.getAll() the list would be sort in the order you specified).
To do what you want consider creating a tab lib as Don suggests here.
Related
I am currently working on a desktop management application with laravel 5.6. According to the management rule a patient can have one or more consultations according to given dates. When I display the list of consultations, I have the same name that repeats, the name that repeats corresponds to the patient who had several consultations, my question of how to avoid this. What I want is the name, and all the dates for these consultations.
class Consultation extends Model
{
public function patient()
{
return $this->belongsTo('App\Models\Patient');
}
}
class Patient extends Model
{
public function consultations()
{
return $this->hasMany('App\Models\Consultation');
}
}
Here is the query :
$consultations = Consultation::all();
The simplest (but not the prettiest) way to do this is to simply find all patients with consultations. Put those patients in an array, and then in your blade you would loop through these patients and show the consultations individually.
Controller Code:
$active_patients = [];
foreach(Patient::all() as $patient) {
if($patient->consultations->count()>0)
array_push($active_patients,$patient);
}
Pass $active_patients to your view, then loop over it as shown below. Obviously, I don't know all of the attribute names for your Patient or Consultation models and you will need to fix html markup as required, but you can get the picture:
#foreach($active_patients as $patient)
<p>{{$patient->name}}:</p>
#foreach($patient->consultations as $consultation)
<p>{{$consultation->date}}</p>
#endforeach
#endforeach
Disclaimer: This is not the most robust way to do this. It's simply the most straightforward approach. The best way to do this is to use scoped queries combined with appended attributes. For instance, you would make a scope on the Patients model for all patients that have a consultation by using the 'whereHas' eloquent query method to find patients that have consultations scheduled. Then you could just reference them directly as ActivePatient rather than having to build an array each time you reference them. You could also append an attribute to the Consultations model that does the same thing and grabs each consultation for the specific users and makes a nested model collection, but that's much more involved. I'd be happy to share that method with you if you want, but the above code would at least provide you with a working method to achieve what you requested.
I have been trying to get my head around these polymorphic relationships all day. I might be over complicating/thinking it but. Can Laravel handle inverse polymorphic relationships? I have a registration flow that can have two types of field Models- normal field and customField.
When I loop through all the fields available it could pull the attributes from either NormalField or CustomField.
<?php
foreach($registrationFlow->fields->get() as $field)
{
echo $field->name; // could be custom field or could be normal field
}
?>
My difficulty is that, the example given in the docs works if you want to assign a photo to either staff or orders, but i want to assign either a customField or a normalField to a registrationFlow
*Edit
If you follow the example for the polymorphic many to many relationship, The tag class contains posts and videos- while i would want just a simple fields() method that relates to customField or normalField dependent on the type
First of all, you should take a look at the updated docs for Laravel 5.1: https://laravel.com/docs/5.1/eloquent-relationships#polymorphic-relations.
I think the difficulty with the example they provide is that the relationship between Photo and Staff/Product are "has-a" relationships, whereas you are trying to model an "is-a" relationship. However, you can model "is-a" essentially the same way. Take a look at this article: http://richardbagshaw.co.uk/laravel-user-types-and-polymorphic-relationships/.
Basically, the strategy is to define a generic model (and a generic table), perhaps in your case Field, that relates to your RegistrationFlow. You then have two subtype models, NormalField and CustomField, that have one-to-one relationships with Field. (there's your "is-a"). Thus, RegistrationFlow is indirectly related to your field subtypes.
Polymorphism comes in when you want to access the specific subtypes:
class Field extends Model {
public function fieldable()
{
return $this->morphTo();
}
}
Your base field table should have fieldable_id and fieldable_type columns defined (see the Eloquent docs).
You can then add methods to NormalField and CustomField that let you access the base model (your "inverse relationship"):
class NormalField {
public function field()
{
return $this->morphOne('Field', 'fieldable');
}
}
class CustomField {
public function field()
{
return $this->morphOne('Field', 'fieldable');
}
}
Usage:
$field = Field::find(1);
// Gets the specific subtype
$fieldable = $field->fieldable;
I'm creating a food menu which the administrator can order/sort by dragging and dropping. This menu consists of multiple categories (ProductCategory) and products (Product).
I'm using HTML5Sortable on the client-side to allow nested d&d. The markup is pretty simple:
<div class="categories">
#foreach($categories as $category)
<div class="category">
#foreach($category->products as $product)
<div class="products">
<div class=""product" data=id="{{ $product->id }}">
{{ $product->name }}
</div>
</div><!-- /products !-->
#endforeach
</div><!-- /category !-->
#endforeach
</div>
And the corresponding javascript:
$('.categories').sortable({
items: '.category'
});
$('.products').sortable({
items: '.product'
});
// Will be called when the user is done repositioning the products and categories
function getOrderedList() {
var data = {};
$('.categories').find('.category').map(function(i) {
var category = $(this);
data[i] = {};
data[i].id = category.data('id');
data[i].products = category.find('.product').map(function() {
return $(this).data('id');
}).get();
});
data = JSON.stringify(data); // Send data to server
}
The function getOrderedList will send a JSON string back to Laravel, which contains the sorted category id's and product id's:
{"0":{"id":1,"products":[2,3,1,4,5,6,7,8,9,10]},"1":{"id":2,"products":[11,12,13,14]},"2":{"id":3,"products":[15,16,17,18]}}
How would I make this work on the back-end? I guess I must store this array somewhere in the database and later find and order the models by the id's?
In short: What is a clean and flexible solution for sorting (nested) models (within Laravel)?
A common convention is Weight, add a field called (Int)Weight on the products table, which is used to define the order of the items.
Once a change in the order occurs you only update the weight field.
When you retrieve the items, you sort them by Weight.
it becomes similar to an Array
Id Name Weight
01 'product 1' 2
02 'product 2' 0
03 'product 3' 1
when you order it by weight you get
product 2
product 3
product 1
it's similar to an array because
$products[0] = 'product 2'
$products[1] = 'product 3'
$products[2] = 'product 1'
Note that if you want to make it even more dynamic, you can create a polymorphic model that can satisfy multiple models.
Please refer to https://laravel.com/docs/5.1/eloquent-relationships#many-to-many-polymorphic-relations
Polymorphic Relations example
Create table Weights (migration example)
$table->increments('id');
$table->integer('value');
$table->integer('weightable_id')->unsigned();
$table->string('weightable_type');
Create model Weight
class Weight extends Eloquent
{
public function weightable()
{
return $this->morphTo();
}
}
now with any other model
class Products extends Eloquent
{
...
public function weight()
{
return $this->morphOne(Weight::class);
}
}
this way you can just add that method to any model you want then you can sort your model with it.
P.S. make sure any model that uses it, creates that relation immediately after creating the model
i do not recommend this method, it's much better if you explicitly define the weight field in the Products table, i understand how much you want your code to be dynamic, but everything comes at a cost
Performance goes down, it's not easy to visualize your code once you establish polymorphic relations, its more like starting to use Jumps instead of Functions
First, the JSON that you are producing shouldn't be an object where the keys are just array indices. Instead it should be an array of objects that looks like this:
[{"id":1,"products":[2,3,1,4,5,6,7,8,9,10]},{"id":2,"products":[11,12,13,14]},{"id":3,"products":[15,16,17,18]}]
Since the products table to product_categories table has an obvious many to one relationship, you'd just use the product_categories_id foreign key on the products table to represent the relationships laid out in your JSON.
In the nested objects of your JSON, every value in the products key array will have a foreign key that corresponds to the id key value in the same nested object (this is the product_category_id column on your products table).
Your API endpoint function would then look something like this:
public function myApiEndpoint(){
$input = Input::get('input');
$input = json_decode($input, true);
foreach($input as $category){
Product::whereIn('id', $category['products'])->update(array(
'product_category_id' => $category['id']
));
}
}
I am updating the model directly in the API controller here, but you should really do any model changes through a repository that's also implementing an interface.
The above will work if you only ever have one menu (with it's categories and products). If you want multiple menus, then you'll need a menus table along with a three way pivot table (with columns menu_id, product_id, and product_category_id).
I just implement this behavior using this library:
https://github.com/spatie/eloquent-sortable
It is very simple to implement, basically you need an extra column to keep the order and the library will do the rest, here is a part of the documentation:
Implement the Spatie\EloquentSortable\Sortable interface.
Use the trait Spatie\EloquentSortable\SortableTrait.
Optionally specify which column will be used as the order column. The default is order_column.
use Spatie\EloquentSortable\Sortable;
use Spatie\EloquentSortable\SortableTrait;
class MyModel extends Eloquent implements Sortable
{
use SortableTrait;
public $sortable = [
'order_column_name' => 'order_column',
'sort_when_creating' => true,
];
...
}
I am trying to get all attributes in one JSON.
Eg:
Consider I have a table "place" with "id","name","description" as attributes.
Now,
I have another table "region" with "id","name","place_id","description" as attributes(or) schema.
There is one to many relationship between place and region.
And now,I define another table "street" with "id","name","region_id" as its schema.
There is again One to Many relationship between "region" and "street".
I want to get all details which comes in one place.If there is some place "1,banglore,garden city" and there are two regions with that place_id as
"{"1,r.t.nagar,1,region in banglore"},{"2,ashok nagar,1,region in banglore"}".Here "1" is foreign key referring id of "banglore's id(1)".
Now,similarly if each region has 3 streets in street table.I want to get all the values in one JSON. I know that we can get this with foreach .But,i want an efficient solution to get all nested relationship to my Place model from Region,Street in JSON.
Thanks.
You first need to define one to many relations like:
class Places extends Eloquent{
....
public function regions()
{
return $this->hasMany('App\Region');
}
...
}
And the same to streets:
class Regions extends Eloquent{
....
public function streets()
{
return $this->hasMany('App\Street');
}
....
}
Note that I have assumed that your models stay within App folder and have corresponding namespaces.
After that you can get you model like:
Place::with('regions.streets')->get();
Which will automatically nest all relations and cast to JSON if you send a json response.
I want to count the number of posts belongs to a tag. Should I use method or dynamic property?
<?php
class Tag extends Eloquent {
public function posts()
{
return $this->belongsToMany('Post');
}
public function postsCount()
{
return count($this->posts);
}
public function getPostsCountAttribute()
{
return count($this->posts);
}
}
So in template should I use dynamic property:
{{ $tag->postCount }}
or method:
{{ $tag->postCount() }}
Excerpt from the documentation of Laravel 4 regarding Eloquent's Dynamic Properties (accessor) in relationships (bold are mine):
Eloquent allows you to access your relations via dynamic properties. Eloquent will automatically load the relationship for you, and is even smart enough to know whether to call the get (for one-to-many relationships) or first (for one-to-one relationships) method. It will then be accessible via a dynamic property by the same name as the relation.
That said, using the method defined for the database relationship or the dynamic property (accessor) will behave differently.
If you issue the post count using the method as follows:
$count = $tag->posts()->count();
That will generate the proper SQL with the COUNT aggregate function.
In the other hand, if you issue the post count using the dynamic property (accessor) as follows:
$count = count($tag->posts);
That will fetch all the posts, convert them to an array of objects, then counting the number of element of the array.
In your case, the choice should depend of the usage of the posts related to a tag. If you just want to count, then use the method and the aggregate function. But, if apart from counting you will be doing something else with those posts, then use the dynamic property (accessor).