[{"data":1,"prerenderedAt":461},["ShallowReactive",2],{"article-2024_06_28_laravel-guard-igonore-on-seeder":3},{"id":4,"title":5,"body":6,"category":448,"createdAt":450,"description":451,"extension":452,"index":453,"meta":454,"navigation":61,"path":455,"publish":61,"seo":456,"series":453,"seriesTitle":453,"stem":457,"tag":458,"thumbnail":453,"updatedAt":453,"__hash__":460},"articles\u002Farticles\u002F2024_06_28_laravel-guard-igonore-on-seeder.md","LaravelのSeederではModelのguardが無効になることで場合によって存在しないカラムへの挿入でエラーとなる",{"type":7,"value":8,"toc":443},"minimark",[9,22,29,33,36,39,145,148,151,268,271,301,304,366,380,397,400,406,425,428,431,439],[10,11,12,13,17,18,21],"p",{},"こんにちはjunです。Laravelのseederでfactoryの",[14,15,16],"code",{},"create()","を使用せず、",[14,19,20],{},"new Model()","からなるモデルメソッドからデータ挿入を行った時に詰まったことがあり、その忘備録です。",[10,23,24,25,28],{},"結論としてSeederではモデルのguardが無効になり、",[14,26,27],{},"fill()","を使用した挿入で存在しないカラムへのinsertが走ってエラーになることが原因でした。背景から話すので、もしさっさと解決策を知りたい方は「解決方法」のとこまで移動してください。",[30,31,32],"h2",{"id":32},"モデルベースで挿入処理をしたい",[10,34,35],{},"Laravelにはダミーデータを挿入するためにseederと呼ばれる挿入スクリプトファイル、factoryというfakerを利用して指定モデルへのダミーデータの定義を作成できます。とりあえずデータを作成してページングや表示の様子を確かめる時に非常に便利です。",[10,37,38],{},"ただしこのfacotryで挿入するときは「DBのinsert処理を走らせる」方法です。factoryと連結したモデルのキャストなどは考慮してくれますが、基本的にはDBのinsert文を作成するような感じです。",[40,41,46],"pre",{"className":42,"code":43,"language":44,"meta":45,"style":45},"language-php shiki shiki-themes material-theme-ocean","\u002F\u002F ※ Laravel6での記述方法です！\n\n\u002F** @var \\Illuminate\\Database\\Eloquent\\Factory $factory *\u002F\n\nuse App\\Models\\Article;\nuse Faker\\Generator as Faker;\n\n$factory->define(Article::class, function (Faker $faker) {\n    return [\n        'title'=>$faker->text(),\n        'content'=>$faker->realText(),\n        'is_active'=>true, \u002F\u002F キャストしてくれる。true => 1\n        'author'=> User::inRandomOrder()->first()->id \u002F\u002F ID番号を指定しないとダメ。\n    ];\n});\n\n","php","",[14,47,48,56,63,69,74,80,86,91,97,103,109,115,124,133,139],{"__ignoreMap":45},[49,50,53],"span",{"class":51,"line":52},"line",1,[49,54,55],{},"\u002F\u002F ※ Laravel6での記述方法です！\n",[49,57,59],{"class":51,"line":58},2,[49,60,62],{"emptyLinePlaceholder":61},true,"\n",[49,64,66],{"class":51,"line":65},3,[49,67,68],{},"\u002F** @var \\Illuminate\\Database\\Eloquent\\Factory $factory *\u002F\n",[49,70,72],{"class":51,"line":71},4,[49,73,62],{"emptyLinePlaceholder":61},[49,75,77],{"class":51,"line":76},5,[49,78,79],{},"use App\\Models\\Article;\n",[49,81,83],{"class":51,"line":82},6,[49,84,85],{},"use Faker\\Generator as Faker;\n",[49,87,89],{"class":51,"line":88},7,[49,90,62],{"emptyLinePlaceholder":61},[49,92,94],{"class":51,"line":93},8,[49,95,96],{},"$factory->define(Article::class, function (Faker $faker) {\n",[49,98,100],{"class":51,"line":99},9,[49,101,102],{},"    return [\n",[49,104,106],{"class":51,"line":105},10,[49,107,108],{},"        'title'=>$faker->text(),\n",[49,110,112],{"class":51,"line":111},11,[49,113,114],{},"        'content'=>$faker->realText(),\n",[49,116,118,121],{"class":51,"line":117},12,[49,119,120],{},"        'is_active'=>true,",[49,122,123],{}," \u002F\u002F キャストしてくれる。true => 1\n",[49,125,127,130],{"class":51,"line":126},13,[49,128,129],{},"        'author'=> User::inRandomOrder()->first()->id",[49,131,132],{}," \u002F\u002F ID番号を指定しないとダメ。\n",[49,134,136],{"class":51,"line":135},14,[49,137,138],{},"    ];\n",[49,140,142],{"class":51,"line":141},15,[49,143,144],{},"});\n",[10,146,147],{},"複雑なリレーションがあったり、モデル内でなんやかんやして他のモデルやデータに変更を与える場合は上記の方法では面倒なことがあります。そのため、facotryで仮のhttpリクエストに相当する連想配列を作成しておき、リクエストボディから挿入処理を行うときのメソッドを経由してデータを作成したい時があります。",[10,149,150],{},"例ではこんな感じ..",[40,152,154],{"className":42,"code":153,"language":44,"meta":45,"style":45},"\u002F\u002F Controller.php\npublic function createData(HogeRequest $request){\n    $m = new TargetModel();\n    $m->add($request->validated());\n    return $m\n}\n\n\n\u002F\u002F TargetModel.php\npublic function add(array $val){\n    $this->fill($val);\n    $this->save();\n    \u002F\u002F complex process...\n    return $this;\n}\n\u002F\u002F ↑このメソッドを使用したい！\n\n\n\u002F\u002F seeder\u002FinsertDummy.php\n$f = factory(App\\Models\\TargetModel.php)->make(); \u002F\u002F データの挿入はせず、fakerで作成した連想配列が取得できます..!\n$m = new TargetModel();\n$m->add($f);\n",[14,155,156,161,166,171,176,181,186,190,194,199,204,209,214,219,224,228,234,239,244,250,256,262],{"__ignoreMap":45},[49,157,158],{"class":51,"line":52},[49,159,160],{},"\u002F\u002F Controller.php\n",[49,162,163],{"class":51,"line":58},[49,164,165],{},"public function createData(HogeRequest $request){\n",[49,167,168],{"class":51,"line":65},[49,169,170],{},"    $m = new TargetModel();\n",[49,172,173],{"class":51,"line":71},[49,174,175],{},"    $m->add($request->validated());\n",[49,177,178],{"class":51,"line":76},[49,179,180],{},"    return $m\n",[49,182,183],{"class":51,"line":82},[49,184,185],{},"}\n",[49,187,188],{"class":51,"line":88},[49,189,62],{"emptyLinePlaceholder":61},[49,191,192],{"class":51,"line":93},[49,193,62],{"emptyLinePlaceholder":61},[49,195,196],{"class":51,"line":99},[49,197,198],{},"\u002F\u002F TargetModel.php\n",[49,200,201],{"class":51,"line":105},[49,202,203],{},"public function add(array $val){\n",[49,205,206],{"class":51,"line":111},[49,207,208],{},"    $this->fill($val);\n",[49,210,211],{"class":51,"line":117},[49,212,213],{},"    $this->save();\n",[49,215,216],{"class":51,"line":126},[49,217,218],{},"    \u002F\u002F complex process...\n",[49,220,221],{"class":51,"line":135},[49,222,223],{},"    return $this;\n",[49,225,226],{"class":51,"line":141},[49,227,185],{},[49,229,231],{"class":51,"line":230},16,[49,232,233],{},"\u002F\u002F ↑このメソッドを使用したい！\n",[49,235,237],{"class":51,"line":236},17,[49,238,62],{"emptyLinePlaceholder":61},[49,240,242],{"class":51,"line":241},18,[49,243,62],{"emptyLinePlaceholder":61},[49,245,247],{"class":51,"line":246},19,[49,248,249],{},"\u002F\u002F seeder\u002FinsertDummy.php\n",[49,251,253],{"class":51,"line":252},20,[49,254,255],{},"$f = factory(App\\Models\\TargetModel.php)->make(); \u002F\u002F データの挿入はせず、fakerで作成した連想配列が取得できます..!\n",[49,257,259],{"class":51,"line":258},21,[49,260,261],{},"$m = new TargetModel();\n",[49,263,265],{"class":51,"line":264},22,[49,266,267],{},"$m->add($f);\n",[10,269,270],{},"例として、id,title,contentのカラムを持つArticleテーブルとManyToManyのリレーションをもつTagsテーブルがあるとしましょう。作成する際のhttp bodyでは",[40,272,274],{"className":42,"code":273,"language":44,"meta":45,"style":45},"[\n    'title'=>'タイトルだよ',\n    'content'=>'文章文章...'\n    'tags'=>[1,3]\n]\n",[14,275,276,281,286,291,296],{"__ignoreMap":45},[49,277,278],{"class":51,"line":52},[49,279,280],{},"[\n",[49,282,283],{"class":51,"line":58},[49,284,285],{},"    'title'=>'タイトルだよ',\n",[49,287,288],{"class":51,"line":65},[49,289,290],{},"    'content'=>'文章文章...'\n",[49,292,293],{"class":51,"line":71},[49,294,295],{},"    'tags'=>[1,3]\n",[49,297,298],{"class":51,"line":76},[49,299,300],{},"]\n",[10,302,303],{},"このようにフロントエンドから送信され、対象のArticleが作成されたのちに指定のTagsとのIDが連携されます。",[40,305,307],{"className":42,"code":306,"language":44,"meta":45,"style":45},"\u002F\u002F Article.php\nprotected $guarded = ['id'];\n\npublic function tag(){\n    return $this->hasMany(\\App\\Models\\Tags);\n}\n\npublic function add(array $val){\n    $this->fill($val);\n    $this->save();\n    $this->sync($val['tags'])\n    return $this;\n}\n",[14,308,309,314,319,323,328,333,337,341,345,349,353,358,362],{"__ignoreMap":45},[49,310,311],{"class":51,"line":52},[49,312,313],{},"\u002F\u002F Article.php\n",[49,315,316],{"class":51,"line":58},[49,317,318],{},"protected $guarded = ['id'];\n",[49,320,321],{"class":51,"line":65},[49,322,62],{"emptyLinePlaceholder":61},[49,324,325],{"class":51,"line":71},[49,326,327],{},"public function tag(){\n",[49,329,330],{"class":51,"line":76},[49,331,332],{},"    return $this->hasMany(\\App\\Models\\Tags);\n",[49,334,335],{"class":51,"line":82},[49,336,185],{},[49,338,339],{"class":51,"line":88},[49,340,62],{"emptyLinePlaceholder":61},[49,342,343],{"class":51,"line":93},[49,344,203],{},[49,346,347],{"class":51,"line":99},[49,348,208],{},[49,350,351],{"class":51,"line":105},[49,352,213],{},[49,354,355],{"class":51,"line":111},[49,356,357],{},"    $this->sync($val['tags'])\n",[49,359,360],{"class":51,"line":117},[49,361,223],{},[49,363,364],{"class":51,"line":126},[49,365,185],{},[10,367,368,369,372,373,375,376,379],{},"モデルでは",[14,370,371],{},"guarded","を指定して、fillが使用できるようにします。リレーション部分のカラムはlaravelが対応してくれます。そのため、",[14,374,27],{},"では",[14,377,378],{},"tags","というカラムに挿入させるようなSQLは生成されなくなります。",[10,381,382,383,386,387,389,390,393,394,396],{},"しかしseederでこのメソッドを使用した時に ",[14,384,385],{},"Array to srting convertion"," というエラーが発生し詰まってしまいました。よくよくエラートレースを見ると、insert文のカラムに",[14,388,378],{},"が存在しているのがわかりました。「",[14,391,392],{},"filable","が正常に機能していないのか？」と予測をたてて調べたらどうやら「seeder中はfactory通りの内容が挿入されるように、",[14,395,371],{},"が無効になりfillの値が全て考慮される」とのことでした。fillableがワイルドカードのような状態となり、tagsがArticlesのカラムとして認識されたことが原因です。",[30,398,399],{"id":399},"解決方法",[10,401,402,403,405],{},"ではseeder中でもモデルのfillable,",[14,404,371],{},"を有効にすれば解決します。その専用のメソッドがあります。",[40,407,409],{"className":42,"code":408,"language":44,"meta":45,"style":45},"use Illuminate\\Database\\Eloquent\\Model;\n\nModel::reguard(); \u002F\u002F←これをfillの前に入れる\n",[14,410,411,416,420],{"__ignoreMap":45},[49,412,413],{"class":51,"line":52},[49,414,415],{},"use Illuminate\\Database\\Eloquent\\Model;\n",[49,417,418],{"class":51,"line":58},[49,419,62],{"emptyLinePlaceholder":61},[49,421,422],{"class":51,"line":65},[49,423,424],{},"Model::reguard(); \u002F\u002F←これをfillの前に入れる\n",[10,426,427],{},"この静的メソッドを呼び出すことでguardedを再度有効にでき、指定・存在するカラムのみにfillが行われます。",[30,429,430],{"id":430},"参考",[10,432,433],{},[434,435,436],"a",{"href":436,"rel":437},"https:\u002F\u002Fstackoverflow.com\u002Fquestions\u002F67092809\u002Ffillable-doesnt-work-when-creating-model-from-seeder",[438],"nofollow",[440,441,442],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":45,"searchDepth":65,"depth":65,"links":444},[445,446,447],{"id":32,"depth":58,"text":32},{"id":399,"depth":58,"text":399},{"id":430,"depth":58,"text":430},[449],"ministack","2024-06-28","LaravelのSeederでModelメソッドを用いて挿入する場合の注意点","md",null,{},"\u002Farticles\u002F2024_06_28_laravel-guard-igonore-on-seeder",{"title":5,"description":451},"articles\u002F2024_06_28_laravel-guard-igonore-on-seeder",[459],"laravel","V44O9yNCFMlfPQN4Yjg8vBnRzTGUlYiKV2q4aF6CG4U",1780987141990]