So I have a Posts model that has many comments and belongs to a user, so when I want to add a comment, which belongs to a post and a user, I must give it a user a id, and this is what I tried.
use App\Posts;
use App\Comment;
class CommentsController extends Controller
{
public function store(Posts $post)
{
$this->validate(request(), ['body' => 'required|min:2']);
$post->addComment(request([
'body' => request('body'),
'user_id' => auth()->user()]));
}
}
But what I am getting is
Type error: Too few arguments to function App\Posts::addComment(), 1
passed 2 expected.
The addcoment method, from the posts model:
public function addComment($body, User $userid)
{
$this->comments()->create(compact('body', 'userid'));
return back();
}
Following this tutorial https://laracasts.com/series/laravel-from-scratch-2017/episodes/19, but the tutor skipped this step.
Your method addComment($body, User $userid) needs 2 arguments!
You should try something like this :
$post->addComment(request('body'),auth()->user());
OR (I'm not sure for this one) This one below will not work.
$post->addComment(request(['body' => request('body')],auth()->user());
Related
In Laravel 8 it is possible to quickly fill relationships with factories. However, I cannot figure out how to generate more than one relationship. How can I create a random or new relationship for each link using the new Laravel 8 syntax?
This factory syntax is only available in Laravel 8.
https://laravel.com/docs/8.x/database-testing#factory-relationships
Problem
Consider the following relationship:
Each link belongs to a website and a post.
Both websites and posts can have many links.
<?php
class Post extends Model
{
use HasFactory;
function links()
{
return $this->hasMany(Link::class);
}
}
class Website extends Model
{
use HasFactory;
function links()
{
return $this->hasMany(Link::class);
}
}
class Link extends Model
{
use HasFactory;
function post()
{
return $this->belongsTo(Post::class);
}
function website()
{
return $this->belongsTo(Website::class);
}
}
What I tried/want
What I tried below will only generate one model for all the links. How can I create a random or new relationship for each link using the new Laravel 8 syntax?
Link::factory()->count(3)->forPost()->forWebsite()->make()
=> Illuminate\Database\Eloquent\Collection {#4354
all: [
App\Models\Link {#4366
post_id: 1,
website_id: 1,
},
App\Models\Link {#4395
post_id: 1, // return a different ID
website_id: 1,
},
App\Models\Link {#4370
post_id: 1, // return a different ID
website_id: 1, // return a different ID
},
],
}
Just add this to your LinkFactory:
public function definition()
{
return [
'post_id' => function () {
return Post::factory()->create()->id;
},
.....
];
}
And now you can create new Post for each new Link:
Link::factory()->count(3)->create();//Create 3 links with 3 new posts
or attach new Links to existing Post:
Link::factory()->count(3)->create(['post_id' => Post::first()->id]); //create 3 links and 0 new posts
In Laravel 9, you can use this macro:
// database/factoryMacros.php
<?php
namespace Database\Support;
use Illuminate\Database\Eloquent\Factories\BelongsToRelationship;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
/** #param Factory|Model $factory */
Factory::macro('hasParent', function (mixed $factory, string $relationship = null): self {
return $this
->state(function () use ($factory, $relationship): array {
$belongsTo = new BelongsToRelationship(
factory: $factory,
relationship: $relationship ?? guessBelongsToMethodName($factory),
);
return $belongsTo
->recycle(recycle: $this->recycle)
->attributesFor(model: $this->newModel());
});
});
Factory::macro('hasChildren', fn (...$arguments): self => $this->has(...$arguments));
Factory::macro('hasChild', fn (...$arguments): self => $this->has(...$arguments));
/** #param Factory|Model $factory */
function guessBelongsToMethodName(mixed $factory): string
{
$modelName = is_subclass_of($factory, Factory::class)
? $factory->modelName()
: $factory::class;
return Str::camel(class_basename($modelName));
}
Usage
Use the method hasParent($factory) instead of for($factory):
// Creates 3 Link, 3 Post, 3 Website
Link::factory()
->count(3)
->hasParent(Post::factory())
->hasParent(Website::factory())
->make();
You can also use hasChildren($factory) or hasChild($factory) instead of has for name consistency:
// Creates 3 Post, 3 Link
Post::factory()
->count(3)
->hasChild(Link::factory())
->make();
The syntax of the macros is the same as for and has.
You can explicitly define the relationship name, pass complex factory chains, pass a concrete model, and use it with recycle, for example.
Installation
Add the file to your composer.json:
{
...
"autoload": {
"files": [
"database/factoryMacros.php"
]
}
}
Run a composer dump-autoload to reload the composer file.
Alternatively, you can register the macro as a service or load it as a mixin.
PS: I intend to create a library for this in the future.
Tests
/**
* Use "DatabaseEloquentFactoryTest.php" as base:
* https://github.com/laravel/framework/blob/de42f9987e01bfde50ea4a86becc237d9c8c5c03/tests/Database/DatabaseEloquentFactoryTest.php
*/
class FactoryMacrosTest extends TestCase
{
function test_belongs_to_relationship()
{
$posts = FactoryTestPostFactory::times(3)
->hasParent(FactoryTestUserFactory::new(['name' => 'Taylor Otwell']), 'user')
->create();
$this->assertCount(3, $posts->filter(function ($post) {
return $post->user->name === 'Taylor Otwell';
}));
$this->assertCount(3, FactoryTestUser::all());
$this->assertCount(3, FactoryTestPost::all());
}
}
TL;DR;
In Laravel 9, it is not possible to achieve this. The for() uses a single model for all instances.
There's a PR to fix this behavior, but the PR was closed, and I'm not sure it will ever be implemented:
https://github.com/laravel/framework/pull/44279
The laravel magic factory method for allows you to populate the database with one record from the foreign table. See link to documentation https://laravel.com/docs/8.x/database-testing#belongs-to-relationships
In your case, using forPost() and forWebsite() will allow you to populate the database with one id from the Post table and the Website table.
If you want to use different IDs use this syntax instead
Link::factory()->count(3)->make()
Had a similar problem and was only able to get it working when I attached within the afterCreating() on a single factory. This allows me to create/store the id of each model and then attach to the Link model
I'm choosing to start with WebsiteFactory but you can also start with PostFactory since those are the "highest parent" models. If you try to make a Link without the website_id and the post_id I believe you will get a error asking for both.
class WebsiteFactory extends Factory
{
public function definition(){...}
public function configure()
{
return $this->afterCreating( function (Website $website){
// the website model is created, hence after-creating
// attach Website to a new Post
$post = Post::factory()->hasAttached($website)->create();
// create new links to attach to both
$links = Link::factory()->for($website)->for($post)->count(3)->create();
});
You can leave PostFactory and LinkFactory as simple definition blocks (or add other stuff if you wanted). Now when you create a new Website via factory, it will create a new post and 3 new links. For example, you can now run
php artisan tinker
$w = Website::factory()->create(); // one website-one post-3 links
$ws = Website::factory()->count(5)->create(); // 5 website-5 post- 15 links
Check out the Factory Callbacks here (9.x docs, but they are in 8.x too):
https://laravel.com/docs/9.x/database-testing#factory-callbacks
\App\Models\Category::factory(10)
->has(Product::factory()->count(10), 'products')
->create();
It would be better if you play around with code. You will understand better.
$user = User::factory()
->has(Post::factory()->count(3), 'posts')
->create();
The above code will create three post for a single user. It will insert three post row and a user row. On the other hand the code below, seems three post will be inserted for user with name Jessica Aercher, that is it won't insert a user.
$posts = Post::factory()
->count(3)
->for(User::factory()->state([
'name' => 'Jessica Archer',
]))
->create();
I'm using VueJS and Laravel for an application I'm developing. I've tried to search here for an answer, but I haven't found anything that works. Most of the times, it's because there's really nothing to return, but I've tried to debug my query quite a bit, and I don't understand why I keep getting a null.
So I'm trying to get information about the student who's logged in, so I'm doing an axios get on a route that executes the following:
public function getByUserId($id) {
//$student = $this->studentRepo->findByUserId($id);
$student = Student::where('user_id', $id)->first();
$inscription = Inscription::where('student_id', $student->id)->first();
$student->careers;
$res = $inscription ? new InscriptionResource($inscription) : '';
return response()->json([
'student' => new StudentResource($student),
'inscription' => $res,
]);
}
The thing is, it doesn't find the student with that user_id. I've checked if the user_id (param: $id) is getting there as expected and it is. I've also tried to get the query via ->toSql() and copy pasted the query on the database to test it and I do get the student I'm trying to search for. Thing is, it's not finding it in Laravel for some reason, I'm not sure why.
My student table does have the attribute "user_id", I've checked.
Student file:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Student extends Model {
use SoftDeletes;
protected $dates = ['deleted_at'];
public function charges() {
return $this->hasMany('App\Models\Payment');
}
}
Add the related column in the relation function
public function charges() {
return $this->hasMany('App\Models\Payment', '');
}
First of all I've started to learn Laravel few weeks ago so sorry if I'm not using the right words while I'll explain my problem.
I'm trying to write some API to retrieve all posts in my DB and inside post's info I'd like to retrieve user's info related to every post (like username, id etc.)
User Model:
public function post()
{ return $this->hasMany(Post::class); }
Post Model:
public function user()
{ return $this->belongsTo(User::class); }
then in my PostResource I try to return the post's data from DB
Post Resource:
public function toArray($request)
{
return [
'id' => $this->id,
'user_id' => $this->user_id,
'user_info' => HERE_I_WANT_USER[USERNAME,ID ETC.]
'body' => $this->name
];
}
and in my PostController I've this function that return my collection:
public function show() {
return PostResource::collection ( Post::get() );
}
So every post is linked to every author thanks to "user_id" value.
Here is my question: What are the steps to achieve this?
I've already red Laravel Doc "https://laravel.com/docs/5.5/eloquent-resources#conditional-relationships" making and UserResource and doing the same steps that I did before, but I'm not able to retrieve any data because my user's info return empty.
I'd like to understand better what are the steps.
You would just call it like it is part of the Post model. Something similar to this:
return [
'id' => $this->id,
'user_id' => $this->user_id,
'user_info' => $this->user
'body' => $this->name
];
I'm assuming that $this is a Post Model. If its not, then you'll want to find it and use it instead like $post->user. Additionally, the name of the method will be whatever your relationship function is called (with out the parenthesis), so if that ever changes, you'll have to update this. Finally, this will return a User Model, so you can interact with it like a normal model ($post->user->username or whatever).
Here is the Laravel Relationship documentaion for further reference.
I currently have a ticket system built and I would like users to view the status of their Open and Closed tickets.
I can query the database and get all tickets to display, but I only want the logged in users tickets to show. How would I achieve this. Been at it a few days now and I'm at a loss.
What I have:
ROUTE (This returns a 404 due to the {ticketId}) / Note: If I remove the {ticketId} from the Route I get the Type error: *Too few arguments to function App\Http\Controllers\TicketsController::openTickets(), 0 passed and exactly 1 expected"*
Route::get('/tickets/open-tickets/{ticketId}','TicketsController#openTickets')->name('open-tickets');
TICKETS CONTROLLER
public function openTickets($ticketId){
$tickets=Classified::find($ticketId);
$ticketId = Auth::user('id');
$tickets = DB::table('tickets')->orderBy('st_id', 'DESC')->where('status', '=', 'OPEN')->where('user_id', '=', $id)->paginate(4);
return view('tickets.open-tickets',compact('tickets'));
Ticket MODEL
class Ticket extends Model
{
use Notifiable, HasPermissionsTrait;
protected $ticketId = 'st_id';
protected $guarded=[];
protected $dispatchesEvents = [
'created' => Events\NewTicket::class
];
}
WORKING QUERY TO DISPLAY ALL TICKETS
public function openTickets(){
$tickets = DB::table('tickets')->orderBy('st_id', 'DESC')->where('status', '=', 'OPEN')->paginate(4);
return view('tickets.open-tickets',compact('tickets'));
I should mention that I changed my id to st_id on the tickets table as I'm using the ID to display the ticket number like this 201804001
Again I'm just looking for a solution to have the logged in user view their tickets and NOT all tickets in the database.
Image of Tickets Table:
You already have a Ticket model but you're using the DB facade to query the tickets table.
Instead, associate Tickets with Users via a one-to-many relationship.
class Ticket extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
In your User class:
public function tickets()
{
return $this->hasMany(Ticket::class);
}
Now you can do $user->tickets()->where('status', 'OPEN')->get(); to get all the open tickets for that user.
Even further, you can add a scope to the Ticket model.
In your Ticket class:
public function scopeOpen($query)
{
return $query->where('status', 'OPEN');
}
This will allow you to make your queries more fluent:
$user->tickets()->open()->get();
To associate a ticket with a user, simply do something along the following lines:
$user->tickets()->save(new Ticket([
'status' => 'open',
'title' => 'Problem logging in',
'body' => 'Lorem ipsum dolor sit amet'
]));
For more information about Eloquent relationships (specifically one-to-many relationships), see the documentation here.
To list all of a user's open tickets, use the following route:
Route::get('tickets/open-tickets/', 'TicketsController#index');
To show an individual ticket, you'll want the following route:
Route::get('tickets/open-tickets/{ticket}', 'TicketsController#show')
Use the following methods in your TicketsController:
public function index()
{
// user() is a helper for Auth::user()
$tickets = user()->tickets()->open()->paginate(4);
return view('tickets.open-tickets', compact('tickets'));
}
public function show(Ticket $ticket)
{
// If the ticket does not belong to the logged in user, abort
if ($ticket->user_id != user()->id) {
abort(403, 'This is not your ticket');
}
return view('tickets.show', compact('ticket'));
}
You'll notice that the show method in your controller type-hints the Ticket model. This is called route-model binding, which is very useful and you can read more about that here.
It may seem like I'm overcomplicating things compared to your original code, but trust me, it's better to understand these things early on! They'll help you a whole lot during development.
I have a problem with displaying data from the form. He wants to load the data from two tables joined the foreign key.
I do not know what I'm doing wrong because I Chaly time returns the message:
Undefined property: Illuminate\Database\Eloquent\Relations\HasMany::$file
offers tabel:
id
user_id
title
...
photosofoffers tabel:
id
offer_id <- primary key offers.id
file (url photos)
...
my view:
#foreach($offers as $offer)
{{ HTML::image($offer->photosofoffers()->file, $offer->title, array('width'=>'50')) }}
my model Offer:
protected $fillable = array('id_category','user_id', 'title', 'description', 'price', 'availability');
public static $rules = array(
'id_category'=>'required|integer',
'title'=>'required|min:2',
'description'=>'required|min:2',
'price'=>'required|numeric',
'availability'=>'integer'
);
public function photosofoffers(){
return $this->hasMany('Photosofoffer');
}
public function category() {
return $this->belongsTo('Category');
}
}
my model Photosofoffer
<?php
class Photosofoffer extends Eloquent {
public function offer(){
return $this->belongsTo('Offer');
}
public function offers() {
return $this->hasMany('Offer');
}
}
How to display ads to load any pictures from another table?
hasMany means there are many photos of one offer. Therefore is it wise to call $something->photosofoffer()->photo ? If the return is an object, you'll definitely get an error.
First do dd($offer->photosofoffers()) or dd($offer->photosofoffers) to see what's happening. Then, if the object is properly being derived, You need to check loop through it. like #foreach($offer->photosofoffers as $photo) your loop of diplaying image #endforeach.
If there is nothing being derived, change the Controller function where you collect the actual $offer and make it Model::with('photoofoffers')->get() or first()
That should clear this up.
Hope this helps.
YK.