Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Shiva's avatar
Level 5

Issues with sending an email with a pdf attachment

I have a page that when the user successfully pays they get sent an email with an invoice.

The problems I'm having is that I keep getting this error

Trying to get property 'name' of non-object

It refers to this line

$user_name = $order->user->name;

I do know that this works because if I dd($user_name) I get the correct name.

The other problem I'm having is that, even though I get the above error I still get sent the invoice, but I don't get one invoice, I get multiple emails with the invoice attached.

This is my successPayment function

public function successPayment(Request $request)
{
    $menus_child = Menu::where('menu_id', 0)->with('menusP')->get();
    $contacts = Contact::all();

    if(Auth::check())
    {
        $orders = Order::all();
        $user_id = Auth::user()->id;

        foreach($orders as $order)
        {
            $exists = Order::find($order->id)->where('user_id', $user_id)->latest()->first();
            $exists->status = 'success';

            $exists->save();

            $invoice_number = Session::get('invoice_number');

            $order_number = Order::where('invoice_number', $invoice_number)->get();

            $user_name = $order->user->name;

            $order_item = unserialize($order->cart);
            $address = json_decode($order->address);

            PDF::setOptions(['defaultFont' => 'sans-serif']);
            $pdf = PDF::loadView('public.pdf.invoice', compact('order', 'user_name', 'order_item', 'address'));
            

            Mail::send('layouts.emails.invoice', [], function($message) use($pdf){
                $message->to('[email protected]', 'Testing PDF order')->subject('Someone has contacted you');
                $message->attachData($pdf->output(), 'invoice.pdf');
            });

        }
    }else{
        return redirect()->route('public.home');
    }

    return view('public.payfast.success', compact('menus_child', 'contacts'));
}

My order confirmation page. This is where the user adds their details and makes sure that the order is correct.

<div class="content_wrapper">
    <h1>Order Confirmation</h1>
    <?php
        $delivery = getDeliveryFee();
    ?>

    {{ $invoice_number }}
    <div class="row">
        <div class="col-lg-12">
            <div class="row">
                <div class="col-lg-12 mt-15">
                    @if($message = Session::get('success'))
                        <div class="alert alert-success" role="alert">
                            {{ $message }}
                        
                            <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                                <span aria-hidden="true">&times;</span>
                            </button>
                        </div>
                    @endif

                    @if($message = Session::get('error'))
                        <div class="alert alert-danger" role="alert">
                            {{ $message }}

                            <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                                <span aria-hidden="true">&times;</span>
                            </button>
                        </div>
                    @endif
                </div>
            </div>
        </div>
    </div>

    <div class="row">
        <div class="col-lg-12">
            <div class="accordion" id="accordionExample">
                <div class="card">
                    <div class="card-header" id="headingOne">
                        <h2 class="mb-0">
                            <button class="btn btn-link" type="button" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
                                Billing Information 
                            </button>
                        </h2>
                    </div>

                    <div id="collapseOne" class="collapse {{ !$errors->any() ? 'show' : '' }}" aria-labelledby="headingOne" data-parent="#accordionExample">
                        <div class="card-body">
                            <div class="row">
                                <div class="col-lg-6">
                                    <div class="address">
                                        @if ($errors->confirmation_errors->has('delivery_address'))
                                            <div class="help-block text-danger">
                                                <strong>Please add an address NOW</strong>
                                            </div>
                                        @endif

                                        @foreach($addresses as $address)
                                            @if(!empty($address->complex))
                                                {{ $address->complex }},
                                            @endif
                                            <div>{{ $address->address }},</div>
                                            <div>{{ $address->suburb }},</div>
                                            <div>{{ $address->city }},</div>
                                            <div>{{ $address->province }},</div>
                                            <div>{{ $address->postal_code }}</div>
                                            
                                            <div class="row edit-delete">
                                                <div class="col-lg-2">
                                                    <a class="btn btn-primary edit-button" href="{{ route('account.edit.delivery.address', [$address->id]) }}">Edit</a> 
                                                    <span>/</span>
                                                </div>

                                                <div class="col-lg-2">
                                                    <form action="{{ route('account.delete.delivery.address', [$address->id]) }}" method="post">
                                                        @csrf
                                                        {{ method_field('DELETE') }}
                                                        <button class="btn btn-danger delete-button"><i class="fa fa-pencil"></i> Delete</button>
                                                    </form>
                                                </div>
                                            </div>
                                        @endforeach
                                    </div>
                                </div>

                                <div class="col-lg-6">
                                    <a href="{{ route('account.add.address') }}" class="btn btn-dark float-right mt-15">Add Address</a>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="card">
                    <div class="card-header" id="headingTwo">
                        <h2 class="mb-0">
                            <button class="btn btn-link collapsed" id="delivery-collection" type="button" data-toggle="collapse" data-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
                                Delivery/Collection
                            </button>
                        </h2>
                    </div>

                    <div id="collapseTwo" class="collapse {{ $errors->confirmation_errors->any() ? 'show' : '' }}" aria-labelledby="headingTwo" data-parent="#accordionExample">
                        <div class="card-body">
                            <p>
                                Please select your delivery option
                            </p>

                            @if ($errors->confirmation_errors->has('delivery_collection'))
                                <div class="help-block text-danger">
                                    <strong>Please select your delivery option</strong>
                                </div>
                            @endif
    
                            <div class="delivery-option">
                                <input type="radio" class="form-check-input {{ $errors->confirmation_errors->has('delivery_collection') ? 'is-invalid' : '' }}" name="delivery-option" id="delivery" value="delivery">
                                <label for="delivery" class="form-check-label">
                                    Delivery
                                </label>

                                <input type="radio" class="form-check-input {{ $errors->confirmation_errors->has('delivery_collection') ? 'is-invalid' : '' }}" name="delivery-option" id="collection" value="collection">
                                <label for="collection" class="form-check-label">
                                    Collection
                                </label>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="card">
                    <div class="card-header" id="headingThree">
                        <h2 class="mb-0">
                            <button class="btn btn-link collapsed" type="button" data-toggle="collapse" data-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
                                Payment Options
                            </button>
                        </h2>
                    </div>

                    <div id="collapseThree" class="collapse {{ $errors->any() ? 'show' : '' }}" aria-labelledby="headingThree" data-parent="#accordionExample">
                        <div class="card-body">
                            @if ($errors->has('payment_option'))
                                <div class="help-block text-danger">
                                    <strong>Please select a payment option</strong>
                                </div>
                            @endif
                            
                            <div class="row">
                                <div class="col-lg-12">
                                    <div class="payment-option">
                                        <input type="radio" class="form-check-input {{ $errors->has('payment_method') ? 'is-invalid' : '' }}" name="payment_method" id="payfast-eft" value="payfast-eft">
                                        <label for="payfast-eft" class="form-check-label">
                                            EFT with PayFast
                                        </label>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="card">
                    <div class="card-header" id="headingThree">
                        <h2 class="mb-0">
                            <button class="btn btn-link collapsed" type="button" data-toggle="collapse" data-target="#collapseFour" aria-expanded="false" aria-controls="collapseThree">
                                Review Your Order
                            </button>
                        </h2>
                    </div>

                    <div id="collapseFour" class="collapse" aria-labelledby="collapseFour" data-parent="#accordionExample">
                        <div class="card-body">
                            <table class="table table-bordered">
                                <thead>
                                    <tr>
                                        <th scope="col">Product</th>
                                        <th scope="col">Code</th>
                                        <th scope="col">Quantity</th>
                                        <th scope="col">Unit Price</th>
                                        <th scope="col">Total</th>
                                    </tr>
                                </thead>

                                <tbody>
                                    @foreach($products as $product)
                                        <?php
                                            $image = getImagesArray($product['item']['image']);
                                        ?>
                                        <tr>
                                            <th>
                                                @if(!empty($image))
                                                    <img src={!! asset("product_images/thumbs/$image[0]") !!}>
                                                @endif

                                                {{ $product['item']['title'] }}
                                            </th>
                                            <td>{{ $product['item']['supplier_code'] }}</td>
                                            <td>{{ $product['qty'] }}</td>
                                            <td>R {{ $product['item']['price'] }}</td>
                                            <td>R {{ $product['price'] }}</td>
                                        </tr>
                                    @endforeach
                        
                                    <tr>
                                        <th colspan="4">
                                            <div class="float-right">
                                                Sub Total       
                                            </div>
                                        </th>

                                        <td id="totalPrice" data-price="{{ $totalPrice }}">
                                            R {{ $totalPrice }}
                                        </td>
                                    </tr>

                                    <tr class="delivery-fees">
                                        <th colspan="4">
                                            <div class="float-right">
                                                Delivery Fee
                                            </div>
                                        </th>

                                        <td id="delivery-price" data-price="{{ $delivery }}">
                                            R {{ $delivery }}
                                        </td>
                                    </tr>

                                    <?php
                                        $total = $totalPrice + $delivery;
                                    ?>

                                    <tr class="total-price">
                                        <th colspan="4">
                                            <div class="float-right">
                                                Total:      
                                            </div>
                                        </th>

                                        <td>
                                            R <span id="completePrice"></span>
                                        </td>
                                    </tr>
                                </tbody>
                            </table>

                            <div class="confirm-order-btn pb-15">
                                @foreach($products as $product)
                                    <!-- BEGIN PAYFAST EFT -->
                                    <div class="payfast-eft" style="display: none">
                                        <form action="{{ route('payment.gateway') }}" method="POST">
                                            @csrf
                                            <input type="hidden" name="merchant_id" value="merchant_id">
                                            <input type="hidden" name="merchant_key" value="merchant_key">
                                            <input type="hidden" name="return_url" value="{{ route('payfast.success') }}">
                                            <input type="hidden" name="cancel_url" value="{{ route('payfast.cancel') }}"> 
                                            <input type="hidden" name="m_payment_id" value="{{ $invoice_number }}">
                                            <input type="hidden" name="amount" class="completePrice" value="">
                                            <input type="hidden" name="item_name" value="{{ $product['item']['title'] }}">
                                            <input type="hidden" name="item_description" value="{{ $product['item']['description'] }}">
                                            <input type="hidden" name="email_confirmation" value="1">
                                            <input type="hidden" name="confirmation_address" value="">
                                            <input type="hidden" name="payment_method" value="payfast_eft">
                                            <input type="hidden" name="delivery_collection" class="delivery_collection" value="">
                                            <input type="hidden" name="delivery_fee" class="delivery_fee" value="{{ $delivery }}">
                                            <!-- THIS IS WHERE THE ADDRESS IS ADDED TO THE HIDDEN FORM -->
                                            <input type="hidden" name="delivery_address" class="delivery_address" value="{{ $address }}">

                                            <?php
                                                $success = url('payfast-success');
                                                $cancel = url('payfast-cancel');
                                                $notify = url('payfast-notify');

                                                $original_str = getAscii('merchant_id=merchant_id&merchant_key=merchant_key&return_url='.$success.'&cancel_url='.$cancel.'&notify_url='.$notify.'&m_payment_id=01AB&amount='.$totalPrice.'&item_name=Test Item&item_description=A test product&email_confirmation=1&[email protected]&payment_method=eft');
                                                $hash_str = hash('MD5', $original_str);
                                                $hash = strtolower($hash_str);
                                            ?>

                                            <input type="hidden" name="signature" value="{{ $hash }}">
                                            
                                            <button type="submit" class="btn btn-success float-right confirm-payfast-order">
                                                Confirm Order
                                            </button>
                                        </form>
                                    </div>
                                    <!-- END PAYFAST EFT -->
                                @endforeach
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
    $(document).ready(function(){
        var price = $("#totalPrice").data('price'); //get data-price by this syntax
        
        $('#completePrice').html(price);
        $('.completePrice').val(price);

        $('input[type="radio"]').click(function(){
            if($(this).attr("value")=="collection"){
                $(".delivery-fees").hide('slow');
                
                var price = $("#totalPrice").data('price'); //get data-price by this syntax
                var deliveryprice = 0;  //get data-price by this syntax
                var totalPrice = parseFloat(price) + parseFloat(deliveryprice);

                $('#completePrice').html(totalPrice);
                $('.completePrice').val(totalPrice);
                $('.delivery_collection').val('collection');
                $('.confirm-order').removeAttr('disabled');
                $('.confirm-payfast-order').removeAttr('disabled');
            }

            if($(this).attr("value")=="delivery"){
                $(".delivery-fees").show('slow');

                var price = $("#totalPrice").data('price'); //get data-price by this syntax
                var deliveryprice = $("#delivery-price").data('price');  //get data-price by this syntax
                var totalPrice = parseFloat(price) + parseFloat(deliveryprice);

                $('#completePrice').html(totalPrice);
                $('.completePrice').val(totalPrice);
                $('.delivery_collection').val('delivery');
            }
        });

        /* BEGIN EFT PAYFAST */
        $('input[type="radio"]').click(function(){
            if($(this).attr("value")=="payfast-eft"){
                $(".payfast-eft").show('slow');
                $(".payfast-card").hide();
                $(".i-pay").hide();
                $(".confirm-order").hide();
                $(".payfast-debit-card").hide();
            }       
        });
        /* END EFT PAYFAST */
    });
</script>   
@stop

When they click confirm they then get sent to a payment gateway. I'm using PayFast as my payment gateway.

If the payment is successfull then they get sent to another page that just says success. The section <input type="hidden" name="return_url" value="{{ route('payfast.success') }}"> passes the url to the controller

public function paymentGateway(Request $request)
{
    if($request->payment_method == 'payfast_eft')
    {
        $process = 'Order Paid';
        $paid = '1';
    }

    if($request->delivery_collection == 'collection')
    {
        $delivery_fee = null;
    }else{
        $delivery_fee = $request->delivery_fee;
    }

    $orders = Order::all();

    $oldCart = Session::get('cart');
    $cart = new Cart($oldCart);

    $validation = Validator::make($request->all(), $this->getRules());

    if($validation->fails())
    {
        return redirect()->route('cart.deliveryConfirmation')
                        ->withErrors($validation, 'confirmation_errors')
                        ->with('error', 'There were validation errors');
    }

    $merchant_id = $request->merchant_id;
    $merchant_key = $request->merchant_key;
    $return_url = $request->return_url;
    $cancel_url = $request->cancel_url;
    $m_payment_id = $request->m_payment_id;
    $amount = $request->amount;
    $item_name = $request->item_name;
    $item_description = $request->item_description;
    $email_confirmation = '1';
    $confirmation_address = '[email protected]';
    $payment_method = $request->payment_method;
    $signature = $request->signature;

    if($request->payment_method == 'payfast_eft')
    {
        $url = 'https://sandbox.payfast.co.za/eng/process?merchant_id='.$merchant_id.'&merchant_key='.$merchant_key.'&return_url='.$return_url.'&cancel_url='.$cancel_url.'&m_payment_id='.$m_payment_id.'&amount='.$amount.'&item_name='.$item_name.'&item_description='.$item_description.'&email_confirmation='.$email_confirmation.'&confirmation_address='.$confirmation_address.'&payment_method='.$payment_method;
    }elseif($request->payment_method == 'cc'){
        $url = 'https://sandbox.payfast.co.za/eng/process?merchant_id='.$merchant_id.'&merchant_key='.$merchant_key.'&return_url='.$return_url.'&cancel_url='.$cancel_url.'&m_payment_id='.$m_payment_id.'&amount='.$amount.'&item_name='.$item_name.'&item_description='.$item_description.'&email_confirmation='.$email_confirmation.'&confirmation_address='.$confirmation_address.'&payment_method='.$payment_method;
    }elseif($request->payment_method == 'dc'){
        $url = 'https://sandbox.payfast.co.za/eng/process?merchant_id='.$merchant_id.'&merchant_key='.$merchant_key.'&return_url='.$return_url.'&cancel_url='.$cancel_url.'&m_payment_id='.$m_payment_id.'&amount='.$amount.'&item_name='.$item_name.'&item_description='.$item_description.'&email_confirmation='.$email_confirmation.'&confirmation_address='.$confirmation_address.'&payment_method='.$payment_method;
    }

    return redirect()->to($url);
}

If I need to provide any other information please let me know

0 likes
13 replies
manelgavalda's avatar

Hey, then the problem must be on the way you are calling the successPayment function. Looks like you are calling it multiple times, but just calling it correctly the first time. Can you show us the code you are using to call it?

munazzil's avatar

You have to use as like below remove the first() and add get() because that will give only first data not a collection otherwise you can't use loop and foreach function,

  foreach($orders as $order)
      {
        $exists = Order::find($order->id)->where('user_id', $user_id)->latest()->get();
     //other codes
      }
Shiva's avatar
Level 5

@MUNAZZIL - if I do that then I get this error

Method Illuminate\Database\Eloquent\Collection::save does not exist.
mvd's avatar

You only do a query if the order exists, not a check.

$exists = Order::find($order->id)->where('user_id', $user_id)->latest()->first();
$exists->status = 'success';

$exists->save();

$invoice_number = Session::get('invoice_number');

$order_number = Order::where('invoice_number', $invoice_number)->get();

$user_name = $order->user->name;
....
....

Change to

$exists = Order::find($order->id)->where('user_id', $user_id)->latest()->first();
if ($exists) {
$exists->status = 'success';

$exists->save();

$invoice_number = Session::get('invoice_number');

$order_number = Order::where('invoice_number', $invoice_number)->get();

$user_name = $order->user->name;
....
....
}
else {
    // order does not exist, do something else
}
mvd's avatar

@shiva can you give us the output from $order->user in the foreach loop?

var_dump($order->user);
Shiva's avatar
Level 5

@MVD - This is what I get

E:\Shiva\Wamp\www\shop\app\Http\Controllers\PublicController.php:497:
object(App\User)[422]
  protected 'fillable' => 
    array (size=16)
      0 => string 'user_type' (length=9)
      1 => string 'salutation' (length=10)
      2 => string 'name' (length=4)
      3 => string 'surname' (length=7)
      4 => string 'email' (length=5)
      5 => string 'password' (length=8)
      6 => string 'phone_number' (length=12)
      7 => string 'telephone_2' (length=11)
      8 => string 'trading_name' (length=12)
      9 => string 'company_name' (length=12)
      10 => string 'vat_number' (length=10)
      11 => string 'ck_number' (length=9)
      12 => string 'office_number' (length=13)
      13 => string 'work_email' (length=10)
      14 => string 'office_address' (length=14)
      15 => string 'contact_person' (length=14)
  protected 'hidden' => 
    array (size=2)
      0 => string 'password' (length=8)
      1 => string 'remember_token' (length=14)
  protected 'connection' => string 'mysql' (length=5)
  protected 'table' => string 'users' (length=5)
  protected 'primaryKey' => string 'id' (length=2)
  protected 'keyType' => string 'int' (length=3)
  public 'incrementing' => boolean true
  protected 'with' => 
    array (size=0)
      empty
  protected 'withCount' => 
    array (size=0)
      empty
  protected 'perPage' => int 15
  public 'exists' => boolean true
  public 'wasRecentlyCreated' => boolean false
  protected 'attributes' => 
    array (size=21)
      'id' => int 11
      'user_type' => string 'customer' (length=8)
      'salutation' => string 'Please select...' (length=16)
      'name' => string 'Test' (length=4)
      'surname' => string 'Testing' (length=7)
      'email' => string '[email protected]' (length=13)
      'email_verified_at' => null
      'password' => string 'y$JqgBrvkAjHspkDpf4HAn3.EHqxbg39RLXLltqk50N12/FqxrJ2bD6' (length=60)
      'phone_number' => null
      'telephone_2' => null
      'trading_name' => null
      'company_name' => null
      'vat_number' => null
      'ck_number' => null
      'office_number' => null
      'work_email' => null
      'office_address' => null
      'contact_person' => null
      'remember_token' => null
      'created_at' => string '2019-04-12 07:28:46' (length=19)
      'updated_at' => string '2019-04-24 05:26:09' (length=19)
  protected 'original' => 
    array (size=21)
      'id' => int 11
      'user_type' => string 'customer' (length=8)
      'salutation' => string 'Please select...' (length=16)
      'name' => string 'Test' (length=4)
      'surname' => string 'Testing' (length=7)
      'email' => string '[email protected]' (length=13)
      'email_verified_at' => null
      'password' => string 'y$JqgBrvkAjHspkDpf4HAn3.EHqxbg39RLXLltqk50N12/FqxrJ2bD6' (length=60)
      'phone_number' => null
      'telephone_2' => null
      'trading_name' => null
      'company_name' => null
      'vat_number' => null
      'ck_number' => null
      'office_number' => null
      'work_email' => null
      'office_address' => null
      'contact_person' => null
      'remember_token' => null
      'created_at' => string '2019-04-12 07:28:46' (length=19)
      'updated_at' => string '2019-04-24 05:26:09' (length=19)
  protected 'changes' => 
    array (size=0)
      empty
  protected 'casts' => 
    array (size=0)
      empty
  protected 'dates' => 
    array (size=0)
      empty
  protected 'dateFormat' => null
  protected 'appends' => 
    array (size=0)
      empty
  protected 'dispatchesEvents' => 
    array (size=0)
      empty
  protected 'observables' => 
    array (size=0)
      empty
  protected 'relations' => 
    array (size=0)
      empty
  protected 'touches' => 
    array (size=0)
      empty
  public 'timestamps' => boolean true
  protected 'visible' => 
    array (size=0)
      empty
  protected 'guarded' => 
    array (size=1)
      0 => string '*' (length=1)
  protected 'rememberTokenName' => string 'remember_token' (length=14)

mvd's avatar

@shiva thanks! And

 var_dump($order->user->name);

in the foreach loop?

Shiva's avatar
Level 5

@MVD - I get

E:\Shiva\Wamp\www\shop\app\Http\Controllers\PublicController.php:497:string 'Test' (length=4)

Test is the name

mvd's avatar

@shiva

Thanks, so the problem is not in ?

It refers to this line

$user_name = $order->user->name;

Shiva's avatar
Level 5

@MVD - Then where is the issue? Because I get that error. It's what is confusing me so much

mvd's avatar

@shiva hard to say because you dumped the $order->user->name and you saw the output ('Test')

When you did the dump, there were no errors?

Please or to participate in this conversation.