vandyczech's avatar

Eloquent relationships - still not understand

I need some advice what I do wrong. I have two tables [ tasks ] and [ task_categories ] How can I determine the basic relationships. Can have the task one category or many? I think that only one, one task = one category. How create relationship with this two tables? For example I need show the differnet data from both tables. Where I have place the primary and foreign key? Please answer me, thank you very much.

TaskController

return view('tasks.index', [ 'tasks' => Task::all() ]);

tasks / index.blade.php

 @foreach( $tasks as $task)
          {{ $task->category_name }}                

Task model

 protected $table = 'task_categories';

    public function task_categories()  {
        return $this->hasMany( '\App\TaskCategory' );
}

TaskCategory model

protected $table = 'tasks';

public function tasks() {
return $this->belongsTo('App\Task');
    }
0 likes
19 replies
bashy's avatar

Controller

$tasks = Task::with('task_categories')->get();

return view('tasks.index', compact('tasks'));

View

// ->task_categories is the relation you included. ->name is the field on the relation table (modify accordingly)
{{ $task->task_categories->name }}
1 like
vandyczech's avatar

Its not working :(

Undefined property: Illuminate\Database\Eloquent\Collection::$task_categories

bashy's avatar

If you have more than one task category, you'll need to do a foreach on it.

@foreach($task->categories as $category)
    {{ $category->name }}
@endforeach
1 like
vandyczech's avatar

still wrong:

at PhpEngine->evaluatePath('D:\wamp\www\example.dev\quickstart\storage\framework\views/64972ee707be72ca44d34a98ccb3aeff3ab7bf89.php', array('__env' => object(Factory), 'app' => object(Application), 'errors' => object(ViewErrorBag), 'tasks' => object(Collection))) in CompilerEngine.php line 59 at CompilerEngine->get('D:\wamp\www\example.dev\quickstart\resources\views/tasks/index.blade.php', array('__env' => object(Factory), 'app' => object(Application), 'errors' => object(ViewErrorBag), 'tasks' => object(Collection))) in View.php line 147

I used:

@foreach($tasks->categories as $category)

{{ $category->name }}
yassiNebeL's avatar

Hello @vandyczech ,

Table Task :

  • id_task
  • id_category
  • ...
  • another attribute

Table TaskCategory :

  • id_category
  • ...
  • another attribute

laravel``` // Task Model public function category() { return $this->belongsTo(App\TaskCategory::class); }

laravel```
// then TaskCategory Model
public function tasks()
{
    return $this->hasMany(A\Task::class);
}

this is one-to-many relationship, of course you could switch to many-to-many relationship using pivot table but it depend

1 like
vandyczech's avatar

I have table tasks and task_categories, table names should be plural or not? And should method name be the same as the table name?

vandyczech's avatar

Now I have this: it is right?

class Task extends Model    {

    public function category(){

    return $this->hasMany( TaskCategory::class );

    }

}

class TaskCategory extends Model {

    public function task()  {

    return $this->belongsTo(Task::class);

    }

}

In tasks table i have category_id colum and in task_categories table i have task_id

thomaskim's avatar

@vandyczech bashy is saying to do this. Loop through the tasks. Then loop through each task's category.

@foreach ($tasks as $task)
    @foreach($task->category as $category)
        {{ $category->name }}
    @endforeach
@endforeach

Edit: Also, because you keep changing the method name, it's hard to say exactly what it is at the moment so you may need to change ->category to reflect the method name.

1 like
jlrdw's avatar

@thomaskim your reply is great for small amounts of data, but for large amounts of data you need pagination included in the inner foreach. This sort of thing is better suited to a single page style type php page where you can control the pagination in the inner foreach. Reports on a large scale are done that way.

@foreach ($tasks as $task)  // this needs to be turned into a header.
    @foreach($task->category as $category)
        {{ $category->name }}  // one cat could have 1000 rows. need to paginate results.
    @endforeach
@endforeach
1 like
jgreen's avatar

Table names are plural, as are many relations. Models are singlular, as are one relations. It helps to say it out loud.

  • A task may have one category which can have many tasks: This is a one-to-many relationship between tasks and task_categories.
  • A task may have many categories which can have many tasks: This is a many-to-many relationship.

Assuming that a task can only have one category, but the categories can be assigned to multiple tasks (one-to-many), here's how it should look:

Migrations

//tasks
class CreateTasksTable extends Migration
{
    public function up()
    {
        Schema::create('tasks', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            ...
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::drop('tasks');
    }
}

//task_categories
class CreateTaskCategoriesTable extends Migration
{
    public function up()
    {
        Schema::create('task_categories', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            ...
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::drop('task_categories');
    }
}

//Pivot
class CreateTaskTaskCategoryTable extends Migration
{
    public function up()
    {
        Schema::create('task_task_category', function (Blueprint $table) {
            $table->integer('task_id')->unsigned();
            $table->integer('task_category_id')->unsigned();
            ...
            $table->timestamps();

            $table->unique(['task_id','task_category_id']);  //Primary Key is the relation and a task and category can only be related once.
            
            $table->foreign('task_id')
                ->references('id')
                ->on('tasks');
            $table->foreign('task_category_id')
                ->references('id')
                ->on('task_categories');
                
        });
    }

    public function down()
    {
        Schema::drop('task_task_category');
    }
}

Models:

class Task extends Model    {

    public function categories(){

    return $this->hasMany( '\App\TaskCategory');

    }

}

class TaskCategory extends Model {

    public function task()  {

    return $this->belongsTo('\App\Task');

    }

}

Controller:

return view('tasks.index', [ 'tasks' => Task::all() ]);

View (index.blade.php):

<ul>
 @foreach( $tasks as $task)
    <li>{{$task->name}}</li>
    <ul>
    @foreach($task->categories() as $category)
        <li>{{$category->name }}  </li>
    @endforeach 
    </ul>
@endforeach 
</ul>
jgreen's avatar

@jlrdw yeah, it should be done, but how it's done is a bit subjective and OP's question was really about getting the relationships working, which is all I addressed.

vandyczech's avatar

@jgreen : Still not working, missing argument for integer() on line 17

 $table->integer('task_category_id')->unsigned();
jlrdw's avatar

@vandyczech have you taken the intermediate - tutorial that's included in the docs yet, I would thoroughly do that and understand what's going on. And as I recall I believe there is a free video on one to many relationships. But good luck.

jlrdw's avatar

As long as your query doesn't return thousands of Records the answer @thomaskim gave should work, why don't you practice his answer and later as you get more advanced you can deal with large amounts of data.

1 like

Please or to participate in this conversation.