1

I am working on Order-Ticket functionality. Many tickets should ideally be generated for a single order.

Earlier, the responsibility of creating tickets was written inside Order Model and that time it worked fine but now that I am trying to move that responsibility to Ticket model itself, it is not working.

I'll start with the Test file 'TicketTest.php'

<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use App\Concert;
use Carbon\Carbon;
use App\Order;

class TicketTest extends TestCase
{
    use DatabaseMigrations;

    /** @test */
    function can_create_multiple_tickets_for_order(){

        $numberOfTickets = 2;

        // Arrange - Create Concert and Order
        $concert = factory(Concert::class)->create(
            [
                'date' => Carbon::parse('December 1, 2016 8:00pm'),
            ]
        );

        $order = $concert->orders()->create([
            'email' => 'abc@gmail.com'
        ]);

        // Act - Create Tickets
        $order->tickets()->createTickets($numberOfTickets); // This line throws call to undefined method.

        // Assert
        $this->assertEquals($numberOfTickets, $order->tickets()->count());

    }
}

'Order.php' Model

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Order extends Model
{
    protected $guarded = [];

    public function tickets()
    {
        return $this->hasMany(Ticket::class);
    }

}

'Ticket.php' Model

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Ticket extends Model
{
    public function createTickets($ticketQuantity){
        foreach(range(1, $ticketQuantity) as $i) {
            $this->create([]);
        }
    }

    public function order(){
        return $this->belongsTo(Order::class);
    }
}
Sumit Pore
  • 51
  • 1
  • 8

1 Answers1

0

Before Laravel 5.4, I solved this problem by creating a new Relation and using that relation to Map Order to Ticket. Created a file 'app/Relation/TicketOrderRelation.php' and added following code in it

<?php
namespace App\Relation;

use Illuminate\Database\Eloquent\Relations\HasMany;

class TicketOrderRelation extends HasMany {

    /**
     * Create a Collection of new tickets
     *
     * @param int $ticketQuantity Number of Tickets to be created 
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public function createTickets($ticketQuantity){
        $instances = [];

        foreach(range(1, $ticketQuantity) as $i) {
            $instances[] = $this->create([]);
        }

        return collect($instances);
    }
}

New 'Order.php' file

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use App\Relation\TicketOrderRelation;

class Order extends Model
{
    protected $guarded = [];

    /**
     * Define a one-to-many relationship. One Order can have many tickets.
     * 
     * This method is duplicate of hasMany method. The only difference is it
     * returns object of TicketOrderRelation class at the bottom instead of 
     * object of HasMany class.
     *
     * @param  string  $related
     * @param  string  $foreignKey
     * @param  string  $localKey
     * @return \App\Relation\TicketOrderRelation
     */
    public function ticketOrderRelation($related, $foreignKey = null, $localKey = null)
    {
        $foreignKey = $foreignKey ?: $this->getForeignKey();

        $instance = new $related;

        $localKey = $localKey ?: $this->getKeyName();

        return new TicketOrderRelation($instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey);
    }

    public function tickets()
    {
        return $this->ticketOrderRelation(Ticket::class);
    }

}

New 'Ticket.php' file

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Ticket extends Model
{

    protected $guarded = [];

    public function order()
    {
        return $this->belongsTo(Order::class);
    }
}

After Laravel 5.4, Eloquent builder started supporting create method which makes creation easy.

This answer shows how to create a custom Builder. I have not yet tried if custom builder solves this problem on Laravel 5.4 or not but if it does, then I would prefer that.

Sumit Pore
  • 51
  • 1
  • 8