#why I am getting error `Call to undefined method App\Models\User::orders()`

167 messages · Page 1 of 1 (latest)

wind hemlock
#

Hello, i am creating a book store website but now i have three tables in relation book, orders, users i made the relation as below code note: i setted the order as peviot

class Book extends Model
{
     public function users(): belongsToMany
    {
        return $this->belongsToMany(User::class)->using(Order::class);
    }
}
class Order extends Pivot
{
    use HasFactory;

    protected $table = 'orders';

    protected $fillable = [
        'order_date',
        'book_id',
        'customer_id',
        'order_status',
        'quantity',
        'price',
    ];

    #[ArrayShape(['order_date' => 'date', 'order_status' => 'integer'])]
    protected function casts(): array
    {
        return [
            'order_date' => 'date',
            'order_status' => OrderStatus::class,
        ];
    }
}

class User extends Authenticatable
{
     public function books() :BelongsToMany
    {
        return $this->belongsToMany(Book::class)->using(Order::class);
    }
}
User::factory(1)
  ->hasAttached(Order::factory(2))
  ->hasAttached(Book::factory(2))
  ->create();
wise wing
#
->hasAttached(Order::factory(2))

will try to create user orders relation, and your User model has no orders relation.

#

From the given info you should add book relation to Order and use Order factory to create order with a book relation, and it will also be available on the User books , though you'll most likely have to specify the foreign keys for it to work as it won't guess the current names

wind hemlock
#

do you mean books relation or book relation

wise wing
#

based on fillable order can have only 1 book

#

something along these lines:

class Book extends Model
{
    use HasFactory;

    public function users(): BelongsToMany
    {
        return $this->belongsToMany(User::class, 'orders', 'book_id', 'customer_id', 'id', 'id')->using(Order::class);
    }
}

class Order extends Model
{
    use HasFactory;

    protected $table = 'orders';

    protected $fillable = [
        'order_date',
        'book_id',
        'customer_id',
        'order_status',
        'quantity',
        'price',
    ];

    public function book(): BelongsTo
    {
        return $this->belongsTo(Book::class);
    }

    #[ArrayShape(['order_date' => 'date', 'order_status' => 'integer'])]
    protected function casts(): array
    {
        return [
            'order_date' => 'date',
            'order_status' => OrderStatus::class,
        ];
    }
}

class User extends Model
{
    use HasFactory;

    public function books(): BelongsToMany
    {
        return $this->belongsToMany(Book::class, 'orders', 'customer_id', 'book_id', 'id', 'id')->using(Order::class);
    }

    public function orders(): HasMany
    {
        return $this->hasMany(Order::class, 'customer_id', 'id');
    }
}

User::factory()
    ->has(Order::factory(2)->has(Book::factory()))
    ->create();
wind hemlock
#

@wise wing
hello i have created the implimintation as you said and here is the full models , i have removed the OrdersItem model from the models folder but when i used this in my seeder file
User::factory(1)->has(Order::factory(2))->create(); it dose not pepolate the orders_item table ,
but i have somhig to say maybe its not got populated by default for two reasons the naming convention of the table it has to be somthing like Books_orders or because there are no model for the orders_item and there are no factory so i have to populate it manually in that case correct me if i am wrong

class Book extends Model
{

    public function orders() :belongsToMany
    {
        return $this->belongsToMany(Order::class,'order_items')->withPivot('quantity','price');
    }
}

class Order extends Model
{

    public function books():BelongsToMany
    {
        return $this->belongsToMany(Book::class,'order_items')->withPivot('quantity','price');
    }

}


little sage
#

the 'order_items' is the pivot table, laravel handles that...

this should work assuming you have a table named: order_items and you have columns for book_id and order_id

#

However, in the above
User::factory(1)->has(Order::factory(2))->create();
you seem to have removed your Book Relationship to Order...

You did not specify that you changed the User Relationship to also use 'order_items', so I can't say for certain if that is right...

You will need to make sure Order has a relation to users and User has a relation to orders

wind hemlock
#

@little sage the relation would be users has many orders and order belong to one user

#

Correct

little sage
#

I"m trying to figure out exactly what you are trying to accomplish...

wind hemlock
#

What I want is clear a many to may relationship between the order table and the books with a peviout order_items

#

the below is what i want to accomplish this is part of ERD that i made for a my book store

#

think it is clear nw a many to many relationship between orders an books

little sage
#

the biggest issue here is the item_id in the pivot table, there's no reason for it to have a primary key....

# Customer
protected $primaryKey = 'customer_id';

public function orders()
{
   return $this->hasMany(Order::class);
}
# Order
protected $primaryKey = 'order_id';

public function customer()
{
  return $this->belongsTo(Customer::class);
}

public function books()
{
  return $this->belongsToMany(Book::class, 'order_items')
    ->withPivot('quantity', 'unit_price');
}
# Book
protected $primaryKey = 'book_id';

public function orders()
{
  return $this->belongsToMany(Order::class, 'order_items')
    ->withPivot('quantity', 'unit_price');
}

None of that should be too complicated...

You're biggest issue, is most likely because you didn't define the primary key as 'order_id', by default laravel will use 'id'... if you are going to modify your primary key name, you have to tell Laravel Model what it is.

#

Personally, I try not to fight the framework setup, granted you can overwrite everything, but then it's just extra steps 😄

wind hemlock
#

i will try this

#

this error happeins when i added he primary key to each table


  SQLSTATE[42S22]: Column not found: 1054 Unknown column 'user_user_id' in 'field list' (Connection: mysql, SQL: insert into `orders` (`order_date`, `user_id`, `order_status`, `user_user_id`, `updated_at`, `created_at`) values (2004-08-06 00:00:00, 2, 4, 1, 2024-09-21 02:42:30, 2024-09-21 02:42:30))

little sage
#

ya, i was afraid of that...

#

laravel tries to use the Model Name + Primary Key to auto create column names... since normally primary_key is 'id', it generates 'user_id', but since primary key is 'user_id', then it creates 'user_user_id', so we will have to overwrite the columns

#

one sec

wind hemlock
#

before i did that i use the convintion of laravel in case of primary key

little sage
#

what does your database have? is it using id or user_id ?

#

because that image shows customer_id and book_id

wind hemlock
#

the database has id

little sage
#

for all the tables?

wind hemlock
#

yes

little sage
#

ok, then just remove the protected $primaryKey lines then

wind hemlock
#

and that is created by the laravel when i firstly used migration to create table

little sage
#

right

#

ok good

#

the only table that shouldn't have the primary key is the order_items

#

you can technically make the combination pk of order_id and book_id if you wanted... with $table->primary([ 'order_id', 'book_id' ]);

#

but it doesn't need it's own auto-incrementing key

wind hemlock
#

yes i can remove the primary key from the orders_item table ,but i cant make order _id and book id as primary because the relation will be between two primary keys this means one to one relation

little sage
#

that's just how the relationship is determined, it's PK -> Reference Column, not PK -> PK

#

you never access the pivot table directly

wind hemlock
#

book_id and order_id a foriegn keys in that case

little sage
#

the reason behind the primaryKey, is that the combination will not be allowed to be used twice...

#

With just foreign keys, there's nothing stopping you from doing order_id: 1, book_id: 1 100x...

wind hemlock
#

i agree i have to do it like that for security reasons but if i did not it sppouse to work

#

do you agree

little sage
#

that' what testing is for anyways 😄 so find out later 😄

wind hemlock
wind hemlock
wise wing
#

Based on the ERD, which is different than the initial code, there's no laravel relation that can give you access to Book from the Customer, unless you dabble with joins on the relation but I never got much luck going that route so it's either - write your own relation, repeat the same customer_id on the order_items table or maybe you just need it in one place and just write the query in the request or wherever needed.

#

Or create yet another table customer_book that tracks all available books to the customer.

#

Personally there's high chance I would go with the last option of customer_book table as there's no reason why a user cannot receive a book without an order.

#

And i'd recommend going that route too as you'll open the doors to doing so much more than tying it to the orders.

wind hemlock
wind hemlock
wise wing
#

Stores aren't that simple usually. If someone returns/refunds a book how would you remove it from the user?

wind hemlock
#

By the way the part that I send is part of the ERD not all of it

wise wing
#

Even with e-books I had some removed from my account, received updated versions and so on.

#

So the order would lie what I bought if it's the only source of truth.

wind hemlock
#

First if you checked my above erd you will see that it has other lines which means that it has other entities so what I send is part of the actual ERD

#

And for the order new versions are bought

#

When you have a specific version of a book and you got this version from a library and there is newer version of that book then you bought it you did not get it for free

#

The owner of the library will not give you the new version free

wise wing
#

But how would you show it to the user without modifying the order in your original implementation?

wise wing
#

I have received a bunch of free updates

#

Gumroad and so on allows it

wind hemlock
#

In that case I don't have to update the order I have to update the book

#

Or make another table for versions

wise wing
#

Ofc at the end of the day it's your decision to make based on the information you know, I'm just putting my experience out there

#

Well if you update the book, it will be updated in the order too

#

Because they're linked

#

Title usually changes of the book, not always, but it does like R2 and such

wind hemlock
#

I can add new book and authorize the user to access it

wise wing
#

And you came around to how will you do it without touching the orders 😬

wind hemlock
#

It is a new book with a different id

#

It not related to the order

wise wing
#

But currently you'll have to create an order with order item for the user to see it without a separate table

wind hemlock
#

I have created an order item already

wise wing
#

There's so huge miscommunication here

#

So, if I bought, say Laravel for beginners and they release an update, add (v1.1), how will you give it to me?

#

If the author says give the update for free

wind hemlock
#

In that case I will check the book by name if the user has the old version then I will authorize him to access the new one

#

Then he will have both

wise wing
#

Authorize how?

#

Talking about database

wind hemlock
#

Spaite permission

wise wing
#

🤔

#

But how will you know he has it?

wind hemlock
#

Check users data through database

wise wing
#

And where will you add that new book? order_items table?

wind hemlock
#

Ok do you have a clue for this problematic issue

#

Because I don't understand what is your problem

wise wing
#

You never answer how you will attach the new book to the user, technically not just "I'll give it to him"

#

And thus my confusion

#

I'm trying to get to the point of how exactly you're thinking of giving it to him without some book_customer table

#

Because you cannot modify existing orders because of invoices etc.

wind hemlock
#

Orders will not be updated because as you said that it has an invoice but I can authorize him to access the book with that newer version by updating the book in the books table

wise wing
#

You'll update the existing book?

wind hemlock
#

When I buy jetbrains subscriptions the order is not updated with newer versions

#

Yes

wise wing
#

That is attached to the order_item? And uses the title etc from the book thus indirectly modifying the order?

#

And modifying the invoice

wind hemlock
#

As I said before I have another option is to add new book withe version and give him access to it

wise wing
#

I mean, if the shop allows you to do that then by all means, I would get scolded just thinking about it.

wind hemlock
#

You are looping and you didn't give me a clue tell me if you are me how can you solve this

wise wing
#

You create a new book and just swap it in the book_customer table, without ever touching the order or invoices and so on.

wind hemlock
#

You said that you are experienced and give me the clue according to your experience

wise wing
#

Well yes, you won"t be tied to orders and have a free ground to add, remove and modify user content, without ever touching on order and invoices, which are fine for normal users but not companies that have to provide those invoices to the gov.

wind hemlock
#

Then ha have to make a relation between books and users

#

One to many relation

wise wing
#

We'll yes, also just because you have an order doesn't mean it's paid so you'll just dodge a bunch of bullets

#

If it"s in the book_customer table, it means he has access, not maybe

wind hemlock
#

And I can make a view in database for the invoice

#

And another one for the cart

wise wing
#

You'll have to do that anyway

#

There's always a user that did not receive the email with invoice and so on

#

Or lost it and year later asks you to resend it

wind hemlock
#

I mean yes this is all in my consideration

wise wing
#

And that is even from subscriptions

wind hemlock
#

I will do it any ways

wise wing
#

Can only wish you good luck, I hate e-commerce

wind hemlock
#

I considered a small e-commerce I am not developing Amazon

wise wing
#

They all start small, but the problems stay the same, just different scale

wind hemlock
#

By the way I am doing it all by my self design database and back end

wise wing
#

We have an invoicing service and my god there's so much BS with gov and with users

#

Doing by yourself is fine, it's impossible to foresee what will come up, just adapt

#

Nobody gains valuable knowledge without pain

wind hemlock
#

Yes I start and then update according to need

wise wing
#

And the best knowledge for me is knowing what not to do, which can only be gained by failing

wind hemlock
#

What do you mean by this sentence

wise wing
#

That the best learning experience, for me, was failing at something, not reading or watching tutorials, you know, you learn what works and what doesn't instead of just works I don't know why

#

I don't mean it in any way that you'll fail or I wish you to fail if it came across as such

#

Even with what works it can take a 180 turn, and that's again a learning experience

wind hemlock
#

That is normal I fail because of falling I succeed

wise wing
#

That's a beautiful saying

wind hemlock
#

I never give up trying to tell I succeed

#

And also I never give up asking tell I get an answer this after trying all of the options

wind hemlock
#

i have anoher question when i create a customer_book this is not a on to many relation its a many to many correct

wise wing
#

yes, that would be a many to many, possibly with extra columns if needed like "received_at" or something but that depends on needs

#

technically it could be not a traditional pivot table but a full table with id, customer_id, book_id etc., but that's for you to decide if there would be any value in that

wind hemlock
#

this is how i will make order using user , note: this is partial because there are payments and shipment and other stuff like adding the books purshased to the userss book collection

        $user = User::create([
            'first_name' => "mina",
            'last_name' => "shaker",
            'email' => "mina@shaker.com",
            'gender' => Gender::Male,
            'DOB' => "1986-12-17",
            'phone_number' => "0100777794",
            'address_1' => "23 abn matrouh-shoupra-cairo",
            'active' => ActiveEnum::Active,
            'password' => "22058149"
        ]);

        $order = $user->orders()->create([
            'order_date' => now(),
            'user_id' => $user->id,
            'order_status' => OrderStatus::Pending,
        ]);

        $userOrder = Order::find($order->id);

        $userOrder->books()->attach(Book::factory(2)->create()->pluck('id'),['quantity' => 2, 'price' => 200]);
#

what do you think

wise wing
#

it does look to be going in a good direction, if we're looking at it as an overview

wind hemlock
#

every on of this will be in its own controller user creation will be in Authentication etc...

#

i made the relation between orders and shipment one to one

#

and between order and payemnts one to many

wise wing
#

that should be fine for most cases, it's ultra rare to split it into multiple shipments unless it's like huge order

wind hemlock
#

hovever the last may be not iditical for bookstrore situation where no customer will buy book in installments

wise wing
#

when dealing with shipments usually it depends on the delivery service, you could receive multiple tracking numbers etc. but that's a really depends situation and not sure how much it would matter here

#

I don't imagine it would be hard to convert it to one to many if needed

wind hemlock
#

i made the relatio between the payments and the order one to many while this is not identical in a book store because no customer will buy a book in installments but i made it like so becuse customer can pay cach on deleivery or pay using creditc card

#

also do you think that i should add the address column to the shipment or just rerive it using the orders table becusr it got the customer id

wise wing
#

i'd recommend leaving the delivery part untill it's clear on the service etc because theres a lot of stuff also

#

delivery cost, comment for delivery, prefered delivery time, deliver to home or postbox and so on

#

you should save address as one to many on customer

#

some customers have multiple addreses, some include workplace and so on

wind hemlock
#

you meean splitting address

#

here it is a shipments entity that i made

#

note : this can will be changed if i made an integration with one of the delivery companys apis

wise wing
#

the shipment will change and I wouldnt bother dealing fully with it, depending on the service some have like a takeaway points (dunno how to call them really) where they also an api of points and user selects on, save its id (given from the service) for them and so on so i'd say at the beginning theres a lot of grey area to plan for

wind hemlock
#

but i want you to notice that this is a small to meduim project so i dont think i will add this kind of apis 😆

wise wing
#

i sure do hope so 😄

#

its why I hate e-commerce

#

theres endless shit

#

i hope its not an internationall store with all the different vats, currencies etc 😬

wind hemlock
#

no i want to make it as simple as mush as i can its local in my contry

#

that s why i started with this small shipments table