Use one table for different resource in Laravel Nova - php

I'm trying to create something like a blogging system inside the Laravel Nova.
I have a table named articles as well as abstract model AbstractArctile.
I also have 3 categories:
News - App\Models\News\Article extending the App\Models\Abstract\AbstractArticle
Digests - App\Models\Digests\Article extending the App\Models\Abstract\AbstractArticle
Offtopic - App\Models\Offtopic\Article extending the App\Models\Abstract\AbstractArticle
The table articles has a field named category, and there are 3 types of categories: news, digests, offtopic.
Besides the extending the abstract model, each resource model also has one attribute defined, which is it's category in the following manner:
/**
* To which category this article belongs to
* #var array
*/
protected $attributes = [
'category' => 'news'
];
I have no problem creating the articles under the specified categories in the Nova, however, instead of showing the articles from specified category, it displays articles from all categories on all resources.
Is there a way to display articles only from certain category on a given resource?
Summary
One abstract model -> 3 resources extending that model (with category attribute defined) -> how to display only items from that category inside the nova resource?

You can make use of Laravel eloquent query scope.
Add global scope like below to all 3 models (App\Models\News\Article, App\Models\Digests\Article, App\Models\Offtopic\Article), which is a easy way to make sure every query for a given model receives category constrains.
protected static function boot()
{
parent::boot();
static::addGlobalScope('category', function (Builder $builder) {
$builder->where('category', 'news'); // Change the value depends on the model
});
}
Hope this will help you.

In Nova, you can use indexQuery,detailQuery and editQuery as well and customize your resource results.
https://nova.laravel.com/docs/4.0/resources/authorization.html#index-filtering

Related

Laravel eloquent multiple table join with filter

There are theree tables in my system.
Students
Articles
categories
Student can write many articles and a article is belong to just one student. And A Article can have only one category.
Controller
public function all_articles_by_student_by_category(Request $request){
$students_id = $request->students_id;
$categories_id = $request->categories_id;
$article_list = Students::find($students_id)->articles->all();
//This return Something like, Select All Articles Written by Damith
}
Model
class Students extends Model
{
protected $fillable = ['id','first_name', 'last_name', 'age', 'created_at', 'updated_at'];
public function articles()
{
return $this->hasMany('App\Articles');
}
}
What I am try to get
Something like, Select All Articles Written by Damith for Technology Category (Category Name should be there)
What I able to do so far
Something like, Select All Articles Written by Damith using $article_list = Students::find($students_id)->articles->all(); (You can find this code from controller)
What I want from you
How do I modify $article_list = Students::find($students_id)->articles->all(); to get, something like, Select All Articles Written by Damith for Technology Category. (Category name must be there in result and it is on category table, and for where condtion you can use the category_id which is i the article table )
First off with what you have done so far the ->all() method is not needed when getting the records for a relation on a model, this would return all of the articles linked to that student:
Students::find($students_id)->articles
Go through Articles Model
You could do something like:
Article::where('student_id', $students_id)
->where('category_id', $category_id)->get();
Which would acheive the result you are after.
Go through Students Model
If you want to go through Students Model you can constrain the relation using the with method.
$student = Students::with(['articles' => function($query) use ($category_id) {
$query->where('category_id', $category_id);
}])->find($student_id);
$filteredArticles = $student->articles
Useful Links
Laravel Docs 5.5 for Eager Loading : https://laravel.com/docs/5.5/eloquent-relationships#eager-loading
When accessing Eloquent relationships as properties, the relationship data is "lazy loaded". This means the relationship data is not actually loaded until you first access the property. However, Eloquent can "eager load" relationships at the time you query the parent model.
Laravel Docs 5.5 for Constraining Eager Loads: https://laravel.com/docs/5.5/eloquent-relationships#constraining-eager-loads
Sometimes you may wish to eager load a relationship, but also specify additional query constraints for the eager loading query.
Something like this should work:
$technologyArticles = Articles::where('student_id', '=', $students_id)->where('category_id', '=', $categories_id)->get();

laravel Eloquent one to many two relationship. OpenCart table style

How to implement information and information_description tables in laravel Eloquent Model ? Some how it need to set language, because a title should be a one record.
$information = App\Information::find(1);
$information->title
tables structure
You can review how to do this, and plenty more, by reading the documents by Laravel. They are a great help and this particular question has an example and everything. Having said that, I'll help with getting you started.
Define a relationship in either model, information or information_description, or both. For brevity, I'll use information only.
Pass the foreign_key and local_key in the hasMany() relationship since it differs from Laravel's default behavior.
So we have a model that now looks like:
class Information extends Model
{
/**
* Get the descriptions for the Information model.
* Note the 2nd and 3rd arguments in the method
* which define foreign_key and local_key.
*/
public function description()
{
return $this->hasMany('App\InformationDescription', 'information_id', 'information_id');
}
}
Now that you have the relationship defined, you can perform your query.
// Get the description for the information
$information = App\Information::find(1)->description;
// Iterate over the results
foreach ($information as $description) {
$description->title;
}
The table naming convention used is a little odd, but if I understand it correctly, this will work. Hope it helps.

CakePHP3 Tree bahavior + translation behavior

Im trying to create a list of categories that need to be translated and display them as a tree structure. But so far no luck, i got tree structure going but when ever i create new category it adds up to the tree but wont display the name because its being translated with i18n and stores in diffirent table...
$categories_list = $this->Categories->find('treeList')->toArray();
This var stores tree it self with names that i have in Categories Table...
$categories_list = $this->Categories->find('translations')->toArray();
And this one gives me the actual translated categories, anyone has any idea how to combine them, CakePhp3 is a new thing for me and i cant find to much documentation about combining those two behaviors.
In order to get translated fields into tree list you need to add TranslateTrait into Category entity, it should look like this:
Link to CookBook about Translate behaviour and TranslateTrait
/src/Model/Entity/Category.php
<?php
namespace App\Model\Entity;
use Cake\ORM\Behavior\Translate\TranslateTrait;
use Cake\ORM\Entity;
class Category extends Entity{
use TranslateTrait;
//translation field must be accessible
protected $_accessible = [
'translations' => true,
];
}
Then you should stack multiple finder methods to achieve your goal, one for Translation Behaviour and one Tree Behaviour
Link to CookBook about how to stack multiple finder methods
/src/Controller/ArticlesController.php
use Cake\I18n\I18n;
public function foo(){
/***
*
* 1) use translation finder
* 2) use treeList finder
* 3) give to treeList finder the translated value to use in the output array as valuePath param
*
***/
$tree_list = $this->Articles->Categories
->find('translations')
->find('treeList', ['valuePath' => '_translations.' . I18n::getLocale() . '.title'])
->toArray();
}
You can leave I18n::getLocale() as it is to automatically get the tree list in current language or replace it with the language you prefer.

Handling revisions - setting attributes or mutators for relation columns

Pseudo code - say I have the models Author, Document, Revisions, Editor.
Author hasMany Document
Document hasMany Revisions
Document hasMany Editors (which are stored in the revision table)
But the following table structure:
Author Model: id, name, email
Document Model: id, author_id, title
Revisions Model: id, document_id, editor_id, text, saved_at
Editor Model: id, name, email
First question - to store the revision history (including which editor changed the text at which time); is this an ideal structure? I want to be able to do $author->documents->where('title', 'Some title')->editor->name;
To access the Editor from the Document - is it worth setting attributes directly in the Document constructor:
public function __construct(array $attributes = [] ){
$this->setRawAttributes(
array_merge($this->attributes,
$this->revisions()->orderBy('saved_at', 'desc')->first()->attributesToArray()
)
);
}
Or use mutators in the model:
public function getEditorIdAttribute($value){
return $this->revisions()->orderBy('saved_at', 'desc')->first()->editor_id;
}
Or is there a better way of handling revisions that's more Laravel/Eloquent-like?
For anyone who comes down this path - I wasn't able to set attributes in the constructor and have them available in the Model so I resorted to using mutators.
To prevent a new query every time a mutator was called (which adds up if you have a handful of mutators) - I used a simple workaround:
// Document Model
class Document extends Eloquent{
$this->latest = ''
// relations etc here
public function getSomeValueAttribute{
$this->getLatest('some_value');
}
public function getAnotherValueAttribute{
$this->getLatest('another_value');
}
public function getLatest($attr){
if(empty($this->latest)) $this->latest = $this->revisions->last();
return $this->latest->getAttribute($attr);
}
}
I'm sure I can extend the getValueAttribute() mutator to keep things DRY, but the above works for me for now, and mutators are called before relations are setup so it works quite well. I'm also able to see all my revisions via $document->revisions->get() or just the latest values via $document->text.

Laravel 4 Create post type relationship

I'm trying to understand the eloquent ORM.
I've created a basic post blog.
I want to add post types to my posts, each post should only have one type.
Post types:
News Post
Video Post
Blog Post
Database structure:
Table: archives
id
title
content
created_at
updated_at
excerpt
deleted_at
status
Table: types
id
name
created_at
updated_at
Table: archive_type
id
archive_id
type_id
created_at
updated_at
Models:
Model: Archive
class Archive extends Eloquent
{
protected $fillable = array('title', 'content', 'excerpt');
public function type()
{
return $this->belongsToMany('Type');
}
}
Model: Type
class Type extends Eloquent
{
protected $fillable = array('name');
}
This works, when running:
Archive::with(array('type'))->orderBy('id', 'DESC')->get();
But it returns a collection, I think this is wrong because it should only return one result.
The other problem I have is how to add a new row to the archive_type database for a new post.
Any help is appreciated.
A collection is always returned whether there is zero or 1 million results. The only exception to this rule is the "find" methods, where they are intended for use as a primary key lookup method, in which case, there can only ever be zero or one results, so it will return the model it finds instead of a collection. The behaviour your experiencing with your lookup is as expected.
If you would like to return the first model, you can place ->first() after your ->get().
To create a new archive type inc. the relation you should do:
// Get our archive and type models
$archive = Archive::with(array('type'))->orderBy('id', 'DESC')->get()->first();
$type = Type::where('name','=','video')->get()->first();
// attach them!
$archive->type()->attach($type);
Edit: How to display in blade template.
To pass the collection (or any data for that matter) through to the view you do this in your controller:
return View::make('yourview')->with('archives', $archives);
To loop through a collection in a blade template use #foreach
#foreach ($archives as $archive)
<p>This is archive id is {{ $archive->id }}</p>
#endforeach
otherwise if you've passed through just one model rather than a collection you can just do
<p>This is archive id is {{ $archive->id }}</p>
If your going to ask "how do i just show the first model from a collection in the view", the simple answer is, get the model out of the collection first then pass it to your view, anything else would require "business logic" in your view.

Categories