ErrorException (E_ERROR) :Trying to get property of non-object. when i trying implode array to string in index.blade.php - laravel

index.blade.php
#php
$p = [];
foreach($p->genres as $genre){
$genres[] = $genre->genres;
}
$genre = implode(",", $genres);
#endphp
<span>{{$genre}}</span>
Controller
public function index()
{
$series = Series::all();
return view('admin/series/index',['series' => $series]);
}
model Genres.php
class Genres extends Model
{
protected $primarykey= 'id';
protected $fillable = ['genres'];
public function series()
{
return $this->belongsToMany(Series::class);
}
}
Model Series.php
class Series extends Model
{
protected $table = 'series';
protected $primarykey='id';
protected $fillable = ['title','country','japanese','year','synonyms','trailer','image','network','rating','duration','genres','aired','directors','screenwriters','score','type','episode','synopsis'];
public function genres(){
return $this->belongsToMany(Genres::class);
}
}
I want to display data from different databases using multiple table relationships, but the data is still a compilation array to bring it up.
and compilation I asked a friend. I was told to use a code like the one in index.blade.php. and return like this
ErrorException (E_ERROR) Try to get properties that are not objects

You are accessing array with -> whereas only the object can be accessed with arrow. To access array keys you have to use $array['key'] approach.
Arrays and objects are two different data types, so to access their inner data, you've to use their respective approaches.

You are looping wrong variables/collections. You need to make some changes to make it work. Below code should solve your problem.
#php
$p = [];
foreach($series as $ser){
foreach($ser->genres as $genre){
$p[] = $genre->genres;
}
}
$gen = implode(",", $p);
#endphp
<span>{{$gen}}</span>

You are setting series from the controller so inside the view you must loop over the series first.
#foreach($series as $s){
// then your code should be take place.
#php
$genres = [];
foreach($s->genres as $genre){
$genres[] = $genre->genres;
}
$genre = implode(",", $genres);
#endphp
<span>{{$genre}}</span>
#endforeach

Related

Laravel Eloquent relationship object stale even though data correct in database

I'm using Laravel 5.7 and have a one-to-one relationship between 2 eloquent models.
I have this simple function that works well, and the correct values persist to the database:
public function saveMarketingOriginInfo(Contact $contact, $data) {
$contact->marketingOrigin()->create($data);
$this->makeOtherChangesByReference($contact->marketingOrigin);
$contact->marketingOrigin->save();
return $contact->marketingOrigin;
}
However, when writing a functional test for it, I noticed that the object that it returns is stale (doesn't have the correct values in its properties).
My tests only pass if I change the return statement to return \App\Models\MarketingOrigin::find($contact->id);.
(MarketingOrigin uses 'contact_id' as primary key.)
What am I doing wrong?
How can I return the same object that was just saved in the previous line ($contact->marketingOrigin->save();) without making a database read query (find())?
Update to respond to comments:
protected $table = 'marketing_origins';//MarketingOrigin class
protected $primaryKey = 'contact_id';
protected $guarded = [];
public function contact() {
return $this->belongsTo('App\Models\Contact');
}
The test:
public function testSaveMarketingOriginInfo() {
$helper = new \App\Helpers\SignupHelper();
$contactId = 92934;
$contact = factory(\App\Models\Contact::class)->create(['id' => $contactId]);
$leadMagnetType = 'LMT';
$audience = 'a60907';
$hiddenMktgFields = [
'audience' => $audience,
'leadMagnetType' => $leadMagnetType
];
$result = $helper->saveMarketingOriginInfo($contact, $hiddenMktgFields);
$this->assertEquals($result->contact_id, $contactId, 'contact_id did not get saved');
$this->assertEquals($result->campaignId, '6075626793661');
$this->assertEquals($result->leadMagnetType, $leadMagnetType);
$marketingOrigin = \App\Models\MarketingOrigin::findOrFail($contactId);
$this->assertEquals($marketingOrigin->adsetId, '6088011244061');
$this->assertEquals($marketingOrigin->audience, $audience);
$this->assertEquals($marketingOrigin, $result, 'This is the assertion that fails; some properties of the object are stale');
}
This is because the relationship has not been loaded yet.
You could try $contact->load('marketingOrigin'); to eager load the relationship:
public function saveMarketingOriginInfo(Contact $contact, $data) {
$contact->marketingOrigin()->create($data);
$this->makeOtherChangesByReference($contact->marketingOrigin);
$contact->marketingOrigin->save();
$contact->load('marketingOrigin'); // <---- eager load the relationship
return $contact->marketingOrigin;
}

Loading view inside a Library, issues with cached vars

I am trying to implement a widgets library using load->view. I know I can use include to call directly the file and avoid the vars cache issues but just wondering why it does not work.
Here is how I have structured my code:
My Controller:
class Page extends MY_Controller {
public $data = array();
public function __construct() {
parent::__construct();
...
$this->load->library('widgetmanager');
}
public function index($slug = '') {
echo $this->widgetmanager->show(2);
echo $this->widgetmanager->show(1);
}
}
My Library
class WidgetManager
{
private $CI;
public function __construct()
{
$this->CI = & get_instance();
}
public function show($widget_id) {
$data = array();
$widget_id = (int)$widget_id;
$this->CI->db->select('*');
$this->CI->db->from('widget');
$this->CI->db->where('id', $widget_id);
$query = $this->CI->db->get();
$item = $query->row_array();
$data['widget_title'] = $item['title'];
$data['widget_content'] = $item['content'];
$widget = $this->CI->load->view('widget/'.$item['source'], $data, TRUE);
$data['widget_title'] = '';
$data['widget_content'] = '';
$this->CI->load->view('widget/'.$item['source'], $data);
return $widget;
}
}
widget 1: Calls widget/content
widget 2: Calls widget/banner
What is happening is, the vars set on the first widget call (they are same name as second widget call), get cached, meaning values from the first call are passed to same call. It is weird because are different views.
I have tried:
Using clear_vars(): $this->CI->load->clear_vars(), before and after doing load->view on the library.
Calling load->view with empty array, null, etc
Tried to add a prefix with the widget slug to the vars (that works, but I have to send in some way the prefix to the view, so it is not possible due cache issue)
Any ideas?
Here is what should work.
(I took the liberty of simplifying your database call making it require much less processing.)
public function show($widget_id)
{
$data = array();
$widget_id = (int) $widget_id;
$item = $this->CI->db
->get_where('widget', array('id' => $widget_id))
->row_array();
$data['widget_title'] = $item['title'];
$data['widget_content'] = $item['content'];
$widget = $this->CI->load->view('widget/'.$item['source'], $data, TRUE);
//clear the cached variables so the next call to 'show()' is clean
$this->CI->load->clear_vars();
return $widget;
}
On further consideration The call $this->CI->load->clear_vars(); is probably pointless because each time WidgetManager::show() is called the $data var is recreated with exactly the same keys. When the $data var is passed to load->view the new values of $data['widget_title'] and $data['widget_content'] will replace the values in the cached vars using those keys.

Laravel API APP Many-Many Relationship, how to return specific information in JSON?

I been trying to figure this out for some time now. Basically i got 2 models ' Recipe ', ' Ingredient ' and one Controller ' RecipeController ' .
I'm using Postman to test my API. When i go to my get route which uses RecipeController#getRecipe, the return value is as per the pic below:
Return for Get Route
If i want the return value of the get route to be in the FORMAT of the below pic, how do i achieve this? By this i mean i don't want to see for the recipes: the created_at column, updated_at column and for ingredients: the pivot information column, only want name and amount column information.
Return Value Format I Want
Recipe model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Recipe extends Model
{
protected $fillable = ['name', 'description'];
public function ingredients()
{
return $this->belongsToMany(Ingredient::class,
'ingredient_recipes')->select(array('name', 'amount'));
}
}
Ingredient Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Ingredient extends Model
{
protected $fillable = ['name', 'amount'];
}
RecipeController
<?php
namespace App\Http\Controllers;
use App\Ingredient;
use App\Recipe;
use Illuminate\Http\Request;
class RecipeController extends Controller {
public function postRecipe(Request $request)
{
$recipe = new Recipe();
$recipe->name = $request->input('name');
$recipe->description = $request->input('description');
$recipe->save();
$array_ingredients = $request->input('ingredients');
foreach ($array_ingredients as $array_ingredient) {
$ingredient = new Ingredient();
$ingredient->name = $array_ingredient['ingredient_name'];
$ingredient->amount = $array_ingredient['ingredient_amount'];
$ingredient->save();
$recipe->ingredients()->attach($ingredient->id);
}
return response()->json(['recipe' => $recipe . $ingredient], 201);
}
public function getRecipe()
{
$recipes = Recipe::all();
foreach ($recipes as $recipe) {
$recipe = $recipe->ingredients;
}
$response = [
'recipes' => $recipes
];
return response()->json($response, 200);
}
API Routes:
Route::post('/recipe', 'RecipeController#postRecipe')->name('get_recipe');
Route::get('/recipe', 'RecipeController#getRecipe')->name('post_recipe');
Thanks Guys!
I think your best solution is using Transformer. Using your current implementation what I would recommend is fetching only the needed field in your loop, i.e:
foreach ($recipes as $recipe) {
$recipe = $recipe->ingredients->only(['ingredient_name', 'ingredient_amount']);
}
While the above might work, yet there is an issue with your current implementation because there will be tons of iteration/loop polling the database, I would recommend eager loading the relation instead.
But for the sake of this question, you only need Transformer.
Install transformer using composer composer require league/fractal Then you can create a directory called Transformers under the app directory.
Then create a class called RecipesTransformer, and initialize with:
namespace App\Transformers;
use App\Recipe;
use League\Fractal\TransformerAbstract;
class RecipesTransformer extends TransformerAbstract
{
public function transform(Recipe $recipe)
{
return [
'name' => $recipe->name,
'description' => $recipe->description,
'ingredients' =>
$recipe->ingredients->get(['ingredient_name', 'ingredient_amount'])->toArray()
];
}
}
Then you can use this transformer in your controller method like this:
use App\Transformers\RecipesTransformer;
......
public function getRecipe()
{
return $this->collection(Recipe::all(), new RecipesTransformer);
//or if you need to get one
return $this->item(Recipe::first(), new RecipesTransformer);
}
You can refer to a good tutorial like this for more inspiration, or simply go to Fractal's page for details.
Update
In order to get Fractal collection working since the example I gave would work if you have Dingo API in your project, you can manually create it this way:
public function getRecipe()
{
$fractal = app()->make('League\Fractal\Manager');
$resource = new \League\Fractal\Resource\Collection(Recipe::all(), new RecipesTransformer);
return response()->json(
$fractal->createData($resource)->toArray());
}
In case you want to make an Item instead of collection, then you can have new \League\Fractal\Resource\Item instead. I would recommend you either have Dingo API installed or you can follow this simple tutorial in order to have in more handled neatly without unnecessary repeatition

Laravel 4: Why can't I use aliases in my laravel code

Here's my code:
public function getReports()
{
$reports=DB::table('reports')
->select(array('*',DB::raw('reports.id','reports.departmentID','reports.officerID','reports.created_at','reports.report','reports.title','department.name','posts.name AS pname')))
->leftjoin('department','reports.departmentID','=','department.id')
->leftjoin('posts','reports.postID','=','posts.id')
->leftjoin('users','reports.officerID','=','users.id')
->leftjoin('events','events.id','=','reports.eventID')
->orderBy('reports.id','DESC')
->get();
$reportsArray=array();
foreach($reports as $row)
{
$reportsArray[]=array(
'id'=>$row->id,
'report'=>$row->report,
'title'=>$row->title,
'departmentName'=>$row->name,
'created_at'=>$row->created_at,
'post'=>$row->pname,
);
}
return View::make('reports.reports')
->with('reports',$reportsArray);
}
I'm getting the following error when I load the view: Undefined property: stdClass::$pname
This is basically a problem because i need to use aliases in many instances. And the error is basically the same when I try to use a column alias with any column. Can anyone see where the problem is with my code?
My Model:
class Report extends Eloquent
{
protected $table = 'reports';
protected $fillable= array(
'title','reportDate',,'departmentID','date','eventID','officerID','postID‌​',
);
}

Setting a table name in a model?

Im trying to pass in a table name to my model, as the model operates on two tables, but has the same methods.
I do it like so:
$this->model = new Emotions(array('section' => 'red'));
And in the model I set the table like:
public function __construct($attributes = array(), $exists = false){
parent::__construct($attributes, $exists);
$this->table = $attributes['section'];
}
But I get the error:
Undefined index: section
Any ideas where I'm going wrong?
Yes i get it, This class maybe running twice.
Please try this.
public function __construct($attributes = array(), $exists = false){
parent::__construct($attributes, $exists);
if(isset($attributes['section'])) {
$this->table = $attributes['section'];
}
}
My personal suggestion
<?php
class Emotions extends Eloquent
{
public function setTableName($name)
{
$this->table = $name;
return $this;
}
}
And you can use like this
$emotion = new Emotions(array('foo' => 'bar'))
->setTableName('blabla')
->save();
add below line to your class.
protected $fillable = array('section');
http://laravel.com/docs/eloquent#mass-assignment

Resources