Janaka Dombawela Janaka Dombawela - 2 months ago 20
PHP Question

Laravel eloquent inverse one to many returns empty

I have following MySQL table structure:

posts
table:

posts: {id(PK), title, content, slug, date, writer_id, created_at, updated_at}


writers
table:


writers: {id(PK), name, type, created_at, updated_at}


Migration classes in
database/migrations
directory:

posts table:

class CreatePostsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->longText('content');
$table->string('slug');
$table->date('date');
$table->date('modified_date');
$table->integer('publish');
$table->integer('trash');
$table->integer('wid');
$table->timestamps();
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('posts');
}
}


Changed the type of column:

class RenamePostColumn extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('posts', function ($table) {
$table->longText('content')->change();
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('posts', function ($table) {
$table->longText('content')->change();
});
}
}


Renamed a column:

class RenamePostColumnWid extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('posts', function ($table) {
$table->renameColumn('wid', 'writer_id')->change();
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('posts', function ($table) {
$table->renameColumn('writer_id', 'wid')->change();
});
}
}


writers table:

class CreateWritersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('writers', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('name');
$table->string('type');
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('writers');
}
}


Following are my modals in
app
directory:

Post.php:

class Post extends Model
{
public function writer()
{
return $this->belongsTo(Writer::class);
}
}


Writer.php:

class Writer extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
}
}


Now I have created a repository class in
app/Repositories
directory.

PostRepository.php:

class PostRepository
{
public function forSingle($slug)
{
return Post::whereSlug($slug)->get();
}
}


I debugged above query with:

return Post::whereSlug($slug)->toSql();


It returns the following query:

select * from `posts` where `slug` = ?


My routes are in
routes/web.php
file.

web.php:

Route::get('/post/{slug}', 'PostController@single');


Finally I have my controller in
app/Http/Controllers
directory.

PostController.php:

use App\Repositories\PostRepository;

class PostController extends Controller
{
protected $post;

function __construct(PostRepository $post)
{
$this->post = $post;
}

public function single($slug)
{
return view('single', [
'post' => $this->post->forSingle($slug)
]);
}
}


I have rendered a view file as follows:

single.blade.php

@if (count($post) > 0)
@foreach ($post as $blog)
<h3><a href="#">{{$blog->title}}</a></h3>
<p>{!!$blog->content!!}</p>
@foreach($blog->writer as $writer)
<span>{{$writer->name}}</span>
@endforeach
@endforeach
@endif


Here is my problem. Everything works fine until I add

@foreach($blog->writer as $writer)
<span>{{$writer->name}}</span>
@endforeach


This section gives me error saying:

Trying to get property of non-object (View:\resources\views\single.blade.php)

I have printed the $blog in view by
{{$blog}}
. It does not return any writer attribute. Can you help me with this?

PS: I have not defined primary key foreign key relationships in MySQL database tables.

Answer

When it is inverse one to many eloquent, we need to explicitly tell that we need the other table data. Changing following in the PostRepository.php fixed the issue.

class PostRepository
{
    public function forSingle($slug)
    {
        return Post::whereSlug($slug)->with('writer')->get();
    }
}