My ultimate goal is to print the contents of a table in a database to a table in HTML using Laravel's ORM.
Beyond that I know how to configure the database file, but is there any other things i need to configure.
From my understanding I need three files. Do i need to make a controller? If so how does that work?
Item Class
Route.php
views.php
Here is what I have so far
Item.php
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Item extends Model {
protected $table = 'items';
protected $primaryKey = 'item_id';
public function products()
{
return $this->belongsTo('item_id', 'item_name', 'item_cost');
}
}
routes.php
<?php
Route::get('Product', function()
{
$products = \App\items::all();
return view('Items', $items);
});
and I have no idea how to create a view in HTML.
I know I'm completely off at this point, but I've read the documentation and I am still completely lost when they reference things like URI and have URL in the same sentence without defining or even linking to it elsewhere in the documentation.
Actually, you have declared your Eloquent ORM like this:
class Item extends Model {
// ...
}
But you are using that model/class like this:
$products = \App\items::all();
In this case, \App\items doesn't exist, it should be:
$items = \App\Item::all(); // <-- Item not items
return view('items', $items); // <-- $items not $products
You asked: Do i need to make a controller?
In this case, no, it'll work fine with the anonymous function but using a Controller is better for many reasons.
Update:
Create a view in resources/views as items.blade.php and you can print out the items through a foreach loop. Check the documentation.
Related
I'm obviously missing something. I thought I was comfortable around laravel relationships...
I've 2 tables, named ratings and ratingdetails. The models are named Rating & Ratingdetail:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Rating extends Model
{
public function ratingdetails()
{
return $this->hasMany('App\Ratingdetail');
}
public function campaigns()
{
return $this->hasMany('App\Campaign');
}
}
and
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Spatie\Translatable\HasTranslations;
class Ratingdetail extends Model
{
use HasTranslations;
public $translatable = ['value'];
public function rating()
{
return $this->belongsTo('App\Rating');
}
}
When I try to access to my Rating model it works fine, but I can't access the relationships; the output is the following, despite there should be 4 Ratingdetails rows...:
{"id":1,"description":"fontawesome","created_at":null,"updated_at":null,"deleted_at":null}
Thank you all for your time !
$rating = Rating::find($request->rating_id);
return $rating->toJson();
In the above line of code, you're never accessing the ratingdetails relationship. They are not included by default, and need to be loaded before being available:
$rating = Rating::with(["ratingdetails"])->find($request->rating_id);
return $rating->toJson();
Including it via with() will "Eager load" the relationship and expose it to be accessed via
console.log(rating.ratingdetails);
// Will contain an array of 4 objects
Before converting to json, you'd be able to access $rating->ratingdetails, but once converted, you lose access unless you have previously loaded the relationship.
Actually I can't answer for this question without having the Models' $fillable attributes, or without DB Tables structures. But I think your tables have following columns:
"raitings" -> "id", "description", "created_at", "updated_at", "deleted_at"
"raitingdetails" -> "id", "raiting_id", "value", ...
In normal way, you need to create OneToMany relation for that 2 tables with foreign key. So in your "raitingdetails" migration you need to have something like this:
$table->unsignedBigInteger('raiting_id')->nullable();
$table->foreign('raiting_id')->references('id')->on('raitings')->onUpdate('cascade')->onDelete('cascade');
Your models are correct, but it not just cool now.. You can improve them by adding $fillable columns and FKs of relations (Note: if you're using traditional foreign key concept, like "partents.id"->"childs.partent_id", then you can leave this part too).
For getting all Rating details of 1 Rating, you can do this:
$rating = Rating::find($rating_id);
$rating_details_of_one = $rating->ratingdetails()->get()->toJson();
If you want to have Rating Details for all actions, you can add Accessor in your Rating model and attach that to $appends like this:
protected $appends = [ 'rating_details' ]; public function
public function getRatingDetailsAttribute() {
return $this->ratingdetails;
}
And in logic parts you can access like this:
$ratings = Rating::find($rating_id); // this will get with their "ratingdetails" relation
Or you can attach accessor on the fly without protected $appends and getRatingDetailsAttribute() function like this:
$rating = Rating::find($rating_id);
$rating_details_of_one = $rating->setAppends([ 'rating_details' ])->get()->toJSON();
If you want to have some Ratings with their details, you can use something like this:
$rating_details_of_many = Rating::where('description', 'fontawesome')->with('ratingdetails')->get()->toJson();
Reason
I got a legacy system with a table containing slugs.
When those records match, it represents some kind of page with a layout ID.
Because these pages can have different resource needs it depends on the layout ID which tables can be joined with.
I use Laravel's Eloquent models.
What I would like is to have a child model that holds the layout specific relations.
class Page extends Model {
// relation 1, 2, 3 that are always present
}
class ArticlePage extends Page {
// relation 4 and 5, that are only present on an ArticlePage
}
However in my controller, in order to know which layout I need, I already have to query:
url: /{slug}
$page = Slug::where('slug', $slug)->page;
if ($page->layout_id === 6) {
//transform $page (Page) to an ArticlePage
}
Because of this I get an instance of Page, but I would like to transform or cast it to an instance of ArticlePage to load it's additional relations. How can I achieve this?
You'll need to look into Polymorphic relations in Laravel to achieve this. A Polymorphic Relation would allow you to retrieve a different model based on the type of field it is. In your Slug model you would need something like this:
public function page()
{
return $this->morphTo('page', 'layout_id', 'id');
}
In one of your service providers, e.g. AppServiceProvider you would need to provide a Morph Map to tell Laravel to map certain IDs to certain model classes. For example:
Relation::morphMap([
1 => Page::class,
// ...
6 => ArticlePage::class,
]);
Now, whenever you use the page relation, Laravel will check the type and give you the correct model back.
Note: I'm not 100% sure on the parameters etc. and I haven't tested but you should be able to work it out from the docs.
If your layout_id is on the Page model, the only solution I see is to add a method to your Page model that is able to convert your existing page into an ArticlePage, or other page type, based on its layout_id property. You should be able to try something like this:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Page extends Model
{
const LAYOUT_ARTICLE = 6;
protected $layoutMappings = [
// Add your other mappings here
self::LAYOUT_ARTICLE => ArticlePage::class
];
public function toLayoutPage()
{
$class = $this->layoutMappings[$this->layout_id];
if (class_exists($class)) {
return (new $class())
->newInstance([], true)
->setRawAttributes($this->getAttributes());
}
throw new \Exception('Invalid layout.');
}
}
What this does is look for a mapping based on your layout_id property, and then it creates a new class of the correct type, filling its attributes with those from the page you're creating from. This should be all you need, if you take a look at Laravel's Illuminate\Database\Eloquent::newFromBuilder() method, which Laravel calls when it creates new model instances, you can see what's going on and how I've gotten the code above. You should be able to just use it like this:
$page = Slug::where('slug', $slug)
->first()
->page
->toLayoutPage();
That will give you an instance of ArticlePage
As far as I know there is no built in function for this.
But you could do something like this.
$page = Slug::where('slug', $slug)->page;
if ($page->layout_id === 6) {
$page = ArticlePage::fromPage($page);
}
And then on the ArticlePage create the static method
public static function fromPage(Page $page)
{
$articlePage = new self();
foreach($page->getAttributes() as $key => $attribute) {
$articlePage->{$key} = $attribute;
}
return $articlePage
}
Depending on your use-case might be smart to create a static method that does this automatically on the relation page() for Slug.
I am using Laravel 5's belongsToMany method to define related tables using an intermediary pivot table. My application is using the eloquent models Tour and TourCategory. In the Tour Model I have:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tour extends Model
{
public function cats(){
return $this->belongsToMany('App\TourCategory', 'tour_cat_assignments', 'tour_id', 'cat_id');
}
}
In my controller I am retrieving all the data from the tour table along with the associated category data using Laravel's with method:
$tours = Tour::with('cats')->get();
That all works fine. The problem is that I don't want the category data in its current raw form, I need to first rearrange it. However I cannot overwrite the cats property without unsetting it first:
public function serveTourData(){
$tours = Tour::with('sections', 'cats')->get();
foreach($tours as $tour){
unset($tour->cats); // If I unset first, then it respects the new value. Why do I need to do this?
$tour->cats = "SOME NEW VALUE";
}
Log::info($tours);
}
Can someone explain the logic behind this please?
To override relations on some model, you can use:
public function serveTourData(){
$tours = Tour::with('sections', 'cats')->get();
foreach($tours as $tour){
$tour->setRelation('cats', "SOME NEW VALUE");
}
Log::info($tours);
}
For laravel 5.4 - setRelation
Of course if you are using laravel >= 5.6, you can unset relations by unsetRelation
I just can't get my head around Relationships in Eloquent. Just when I think I've got it, I stumble.
Like here, I want to list out the country field for each Headquarters_Pay_Data item.
Models;
<?php
namespace Datamate;
use Illuminate\Database\Eloquent\Model;
class Headquarters_Pay_Data extends Model
{
//
protected $table = 'headquarters_pay_data';
public function postcode()
{
return $this->hasOne('Datamate\Postcode_Quarter', 'postcode', 'Vendor ZIP');
}
}
And this one;
<?php
namespace Datamate;
use Illuminate\Database\Eloquent\Model;
use Datamate\Country;
class Postcode_Quarter extends Model
{
public $table = '201502_postcode';
protected $fillable = ['country'];
}
My controller;
public function index()
{
//
$headquarters_pay_data = Headquarters_Pay_Data::limit(12)->get();
foreach ($headquarters_pay_data as $key => $value) {
//print $value->postcode->country; //this returns an error. Trying to get property of non-object
print "<br><br>";
print $value->getAttribute('Vendor ZIP');
print "<br><br>";
print $value->postcode; //this is JSON?! Why?
print "<br><br>";
}
Example print out of what looks like JSON even though I haven't asked for JSON;
RH108PJ
{"postcode":"RH108PJ","county":"E10000032","district":"","ward":"","health":"E18000008","gor":"J","parlc":"E14000652","locauth":"E07000226","wardcode":"E05007639","country":"E92000001","gor_new":"E12000008","pct":"E16000108"}
To clarify... How do I just print out the country for each payment?
The postcode field, due to your eloquent relationship method, is an instance of another model (Postcode_Quarter) on the Headquarters_Pay_Data model. So $value->postcode returns that model (as a PHP object). Your turning that model to a string (by printing it) makes it try to convert itself to the best possible format for use as a string, which is a JSON string.
However, you can access the properties of that model, and because you want the country you can do the following:
public function index()
{
$headquarters_pay_data = Headquarters_Pay_Data::with('postcode')->limit(12)->get();
foreach ($headquarters_pay_data as $key => $value) {
print $value->postcode->country;
}
}
You'll notice that in this example we also use with() in order to 'eager load' the postcode relationship. This, generally, makes your queries more efficient, especially in the case where you have a lot of the Headquarters_Pay_Data models, and not very many Postcode_Quarter models, but it is not required to have this work.
Please read the documentation for more information on eager loading.
For a school project, I'm creating a website in the Laravel framework.
I can't work with the data from a many-to-many table in my controller.
Here are my models:
class Item extends Eloquent {
public function Tags()
{
return $this->belongsToMany('Tag', 'items_has_tags', 'items_id', 'tags_id');
}
}
class Tag extends Eloquent {
public function LearningMaterials()
{
return $this->belongsToMany('LearningMaterial', 'learning_materials_has_tags', 'tags_id', 'learning_materials_id');
}
}
I want to iterate all tags from my items in my controller:
//get all items
$all = Item::where('public', '1')->get();
foreach($allLm as $item){
$tags = $item->Tags();
var_dump('will iterate');
foreach($tags as $t){
var_dump('will not iterate');
}
}
What is wrong with my code? How can I handle the tags from my items?
FYI: I'm creating a search engine. If the user inserts an input value like "mana", all items with tag "management" should be shown.
Laravel's belongsToMany method queries related models and doesn't get them. That's because you might want to have some additional constraints in your query. What you need to do is:
$tags = $item->Tags()->get();
Or simply:
$tags = $item->Tags;
Calling the relationship function will return a relation object (in this case an instance of BelongsToMany). You can use this to run add further components to your query before running it.
For example:
$only4Tags = $item->Tags()->take(4)->get();
If you want the result of your relation, call get() or simpler, just use the magic property:
$tags = $item->Tags()->get();
$tags = $item->Tags;