こちらのサイトを参考にしながら、ポートフォリオ作成をしていたところ、表題のエラーが出てしまいつまずいてしまったので備忘録として共有ししたいと思います。(一部コードは省略している部分があります。)
やりたかったこと
index.blade.phpに表示されているフォルダ一覧(親)に関連付けられたタスク(子)を保存したかった。
「タスクを追加する」
{{ route('tasks.create', ['folder' => $current_folder_id])}}" class="btn btn-default btn-block">
をクリックするとTaskController.phpのshowCreateFormメソッドが発動し、create.blade.phpが返される予定だった。
流れとしては
index.blade.phpの「タスクを追加する」を押す→コントローラーのshowCreateFormメソッドが飛び出される→create.blade.phpに遷移→項目を入力し送信→コントローラーのcreateメソッドが呼び出される→index.blade.phpへリダイレクト
のような感じです。
@extends('layout')
@section('content')
<div class="container">
<div class="row">
<div class="col col-md-4">
<nav class="panel panel-default">
<div class="panel-heading">フォルダ</div>
<div class="panel-body">
<a href="{{ route('folders.create') }}" class="btn btn-default btn-block">
フォルダを追加する
</a>
</div>
<div class="list-group">
@foreach($folders as $folder)
<a href="{{ route('tasks.index', ['folder' => $folder->id]) }}" class="list-group-item {{ $current_folder_id === $folder->id ? 'active' : '' }}">
{{ $folder->title }}
</a>
@endforeach
</div>
</nav>
</div>
<div class="column col-md-8">
<!-- ここにタスクが表示される -->
<div class="panel panel-default">
<div class="panel-heading">タスク</div>
<div class="panel-body">
<div class="text-right">
<a href="{{ route('tasks.create', ['folder' => $current_folder_id])}}" class="btn btn-default btn-block">
タスクを追加する
</a>
<?php
namespace App\Http\Controllers;
use App\Models\Folder;
use App\Http\Requests\CreateTask;
use App\Http\Requests\EditTask;
use App\Models\Task;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class TaskController extends Controller
{
/**
* タスク一覧
* @param Folder $folder
* @return \Illuminate\View\View
*/
public function index(Folder $folder)
{
// ★ ユーザーのフォルダを取得する
$folders = Auth::user()->folders()->get();
// 選ばれたフォルダに紐づくタスクを取得する
$tasks = $folder->tasks()->get(); // ★
return view('tasks/index', [
'folders' => $folders,
'current_folder_id' => $folder->id,
'tasks' => $tasks,
]);
}
/**
* タスク作成フォーム
* @param Folder $folder
* @return \Illuminate\View\View
*/
public function showCreateForm(Folder $folder)
{
// echo ('\n');
// echo ('\n');
// var_dump(
// $folder
// );
return view('tasks/create', [
// 'folder_id' => $folder->id,
'folder' => $folder->id,
]);
}
/**
* タスク作成
* @param Folder $folder
* @param CreateTask $request
* @return \Illuminate\Http\RedirectResponse
*/
public function create(Folder $folder, CreateTask $request)
{
$task = new Task();
$task->title = $request->title;
$task->due_date = $request->due_date;
$folder->tasks()->save($task);
return redirect()->route('tasks.index', [
// 'id' => $folder->id,
'folder' => $folder->id,
]);
}
<form action="{{ route('tasks.create', ['folder' => $folder->id]) }}" method="POST">
@csrf
<div class="form-group">
<label for="title">タイトル</label>
<input type="text" class="form-control" name="title" id="title" value="{{ old('title') }}" />
</div>
<div class="form-group">
<label for="due_date">期限</label>
<input type="text" class="form-control" name="due_date" id="due_date" value="{{ old('due_date') }}" />
</div>
<div class="text-right">
<button type="submit" class="btn btn-primary">送信</button>
</div>
</form>
Route::group(['middleware' => 'auth'], function () {
Route::get('/', 'App\Http\Controllers\HomeController@index')->name('home');
Route::get('/folders/create', 'App\Http\Controllers\FolderController@showCreateForm')->name('folders.create');
Route::post('/folders/create', 'App\Http\Controllers\FolderController@create');
Route::group(['middleware' => 'can:view,folder'], function () {
Route::get('/folders/{folder}/tasks', 'App\Http\Controllers\TaskController@index')->name('tasks.index');
Route::get('/folders/{folder}/tasks/create', 'App\Http\Controllers\TaskController@showCreateForm')->name('tasks.create');
Route::post('/folders/{folder}/tasks/create', 'App\Http\Controllers\TaskController@create');
Route::get(
'/folders/{folder}/tasks/{task}/edit',
'App\Http\Controllers\TaskController@showEditForm'
)->name('tasks.edit');
Route::post('/folders/{folder}/tasks/{task}/edit', 'App\Http\Controllers\TaskController@edit');
});
});
Auth::routes();
問題のエラー「Attempt to read property "id" on int」
ネットで検索するとデータを引っ張ってきてるモデルのリレーションがうまくいっていないとの記事がヒットしTask.phpにbelogTo()メソッドを追加してみたのですが解決しませんでした。
参考:https://stackoverflow.com/questions/65864674/attempt-to-read-property-email-on-int-laravel-8
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Folder extends Model
{
use HasFactory;
public function tasks()
{
return $this->hasMany('App\Models\Task', 'folder_id', 'id');
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;
class Task extends Model
{
use HasFactory;
/**
* 状態定義
*/
const STATUS = [
1 => ['label' => '未着手', 'class' => 'label-danger'],
2 => ['label' => '着手中', 'class' => 'label-info'],
3 => ['label' => '完了', 'class' => ''],
];
public function taskToFolder()
{
$this->belongsTo('\App\Models\Folder', 'folder_id', 'id');
}
/**
* 状態のラベル
* @return string
*/
public function getStatusLabelAttribute()
{
// 状態値
$status = $this->attributes['status'];
// 定義されていなければ空文字を返す
if (!isset(self::STATUS[$status])) {
return '';
}
return self::STATUS[$status]['label'];
}
/**
* 状態を表すHTMLクラス
* @return string
*/
public function getStatusClassAttribute()
{
// 状態値
$status = $this->attributes['status'];
// 定義されていなければ空文字を返す
if (!isset(self::STATUS[$status])) {
return '';
}
return self::STATUS[$status]['class'];
}
/**
* 整形した期限日
* @return string
*/
public function getFormattedDueDateAttribute()
{
return Carbon::createFromFormat('Y-m-d', $this->attributes['due_date'])
->format('Y/m/d');
}
}
解決法
teratailで質問したところ5分で回答してくださった方がいらっしゃいました(本当にありがとうございます。)
ベストアンサーをそのまま記述させていただくと
「showCreateFormでは、'folder' => $folder->idと書いてあり、
create.blade.phpで$folderは整数なので、idは存在しません。
そのままcreate.blade.phpで$folder使う:
或いはTaskController.php@showCreateFormで$folderオブジェクトを渡して、idを使うかですね
:view('tasks/create',['folder' => $folder]);」
とのことでした。
後者のコントローラーの記述を
view('tasks/create',['folder' => $folder->id]);
↓
view('tasks/create',['folder' => $folder]);
に変えたところエラーは解消され、本来やりたかった1対多のリレーションに基づいたタスク追加機能を実装することができました。