Mailable: Non Numeric Value Encountered

Published 1 month ago by vincej

I need to pass an alpha-numeric variable $ref into the email I need to send to the customer. This references their quotation data. Example: jac123456-10.

However when it hits the Mail class I get the error. See Here:

class SendCustomerQuote extends Mailable
{
    use Queueable, SerializesModels;

    public function __construct($ref)
    {
        $this->ref = $ref;
    }


    public function build()
    {
        return $this->markdown('emails.customer.customer_quote'/$this->ref);  // HERE IS THE ERROR. 
    }
}

I have read on SO that this is a "feature" of PHP 7.1 & 7.2

So - How do I get around this problem, with out changing all my references to a pure numeric??

Many thanks !

Best Answer (As Selected By vincej)
Cronix

You don't need to pass variables to the view. You make them public properties of your mailable class. All public properties are passed to the view automatically.

class SendCustomerQuote extends Mailable
{
    use Queueable, SerializesModels;

    public $ref; // create public property as $ref

    public function __construct($ref)
    {
        $this->ref = $ref; // assign value to $this->ref
    }

    public function build()
    {
        return $this->markdown('emails.customer.customer_quote'); 
    }
}

then in the emails.customer.customer_quote view, you just access $ref like you normally would, {{ $ref }} etc.

https://laravel.com/docs/5.5/mail#view-data

Snapey
Snapey
1 month ago (993,535 XP)

why do you have a divide symbol there?

Cronix
Cronix
1 month ago (727,800 XP)

You don't need to pass variables to the view. You make them public properties of your mailable class. All public properties are passed to the view automatically.

class SendCustomerQuote extends Mailable
{
    use Queueable, SerializesModels;

    public $ref; // create public property as $ref

    public function __construct($ref)
    {
        $this->ref = $ref; // assign value to $this->ref
    }

    public function build()
    {
        return $this->markdown('emails.customer.customer_quote'); 
    }
}

then in the emails.customer.customer_quote view, you just access $ref like you normally would, {{ $ref }} etc.

https://laravel.com/docs/5.5/mail#view-data

vincej
vincej
1 month ago (39,600 XP)

@snapey Thanks for your reply.. The forward slash was my attempt at creating a url.

@Cronix Thanks for that. Your instructions allowed me to eliminate the non numeric problem. However, I have a new problem as follows:

  Mail::to($customer['email1'])->send(new SendCustomerQuote());

In this line I need to send within the email a very specific customer reference number such that when they click on the email, the controller accesses specifically their Quote.

Therefore I need to pass into the SendCustomerQuote class a specific reference.

I have tried gazzilions of different statements for the ->send(new SendCustomerQuote()) and none of them work. So, for example:

SendCustomerQuote($ref);

SendCustomerQuote($this->ref);

SendCustomerQuote(save_quote($this->ref));

And they all fall.

I have looked at the laravel docs ( thank you !) and the order they use assumes there is only 1 order in their table. In my quotations table I have dozens of quotations which are all accessed via a reference number.

How do I specifically get the reference number into the final email so that when the use clicks on a button it will go to the set controller with the reference number ?

Many thanks !!

Snapey
Snapey
1 month ago (993,535 XP)

Hi Vince

Where you send the email it is as you have now, but you have to pass the data you want the mail to use into the constructor

  Mail::to($customer['email1'])->send(new SendCustomerQuote($ref));

I don't understand the context of the $ref in this case but you may be better passing the whole quote into there so that you can access any information about the quote, or related models. Eg, the quote might have a title, or you might want the customer's full name.

So perhaps

  Mail::to($customer['email1'])->send(new SendCustomerQuote($quote));

Now you are passing the quote into the mailable. In there you need to persist it to a public property

    public $quote;

    public function __construct($quote)
    {
        $this->quote = $quote;
    }

    public function build()
    {
        return $this->markdown('emails.customer.customer_quote'); 
    }

Now in the mail template you can use $quote and any of its attributes or relationships

eg

Your reference number is {{ $quote->ref }}

Hope thats a little clearer

vincej
vincej
1 month ago (39,600 XP)

HI Snapey, Thanks for all your help. I'm still screwing up, however, I believe I know where I am screwing up! :o)

This is where I am going wrong:

Mail::to($customer['email1'])->send(new SendCustomerQuote($newQuotation));

Essentially $newQuotation is not correct and is giving me this error:

Argument 1 passed to App\Mail\SendCustomerQuote::__construct() must be an instance of App\QuotationController, instance of App\Models\Quotation given, called in /var/www/auburntree/app/Http/Controllers/QuotationController.php on line 188

Ok - I think I understand what the error is telling me, "must be an instance of App\QuotationController". But here is my issue. I don't know how to do this. I've tried everything I can think of. My controller has a gazzilion functions in it. I have tried referencing my constructor values, but that is why it is giving the error.

How do I isolate specifically what it wants?

Many thanks, Vince

jimmck
jimmck
1 month ago (62,225 XP)

@vincej Emails have no idea of your servers PHP class structure. What does your URL specifically look like as formatted in the email? Does the URL contain enough unique information to identify the order?

Snapey
Snapey
1 month ago (993,535 XP)

Presumably in the constructor of the mailable you have declared the type of object it should receive ?

jimmck
jimmck
1 month ago (62,225 XP)

@Snapey @vincej Then you could route those URL's to a different class of constructor. You can construct the email to use a whole different set of Controllers than what a normal webpage might call. Share the backend processing once it has been properly routed.

Snapey
Snapey
1 month ago (993,535 XP)

routing has got nothing to do with it

vincej
vincej
1 month ago (39,600 XP)

Frankly guys, I have no idea what I am doing here. My mailable constructor looks like this:

protected $quotation

  public function __construct(QuotationController $quotation)
    {
        $this->quote = $quotation;
    }

I have tried changing the mail::to to this, ie an instance of my function in my controller and I still get an error, although a different error:

 Mail::to($customer['email1'])->send(new SendCustomerQuote($this->save_quote($ref)));

Resulting Error:

"Argument 1 passed to App\Http\Controllers\QuotationController::save_quote() must be an instance of Illuminate\Http\Request, string given, called in /var/www/auburntree/app/Http/Controllers/QuotationController.php on line 188 ◀"

Using this, it wants to see an instance of Request, however, $ref is not part of Request, it is generated through code.

Sorry to be a pain. Many Thanks !!

Snapey
Snapey
1 month ago (993,535 XP)

you need to pass in the model or just your reference. Not the controller !

show where you call the mailable in context. I assume this is within a controller?

vincej
vincej
1 month ago (39,600 XP)

Sure, I passed in the model first time around, and it complained that I had passed in the model and it wanted an instance of the controller. Ok, so I give it an instance of the controller and it complains again, saying I passed in a string . Clearly I am doing something very stupid here. Ok here is an abbreviated Controller:

class QuotationController extends Controller
{

    protected $quotation;
    protected $workorder;
    protected $categories;
    protected $project_summary;
    protected $project_templateDetails;
    protected $save_quote;


    
    public function __construct(Quotation $quotation, Workorder $workorder, Nested_Categories $categories, Project_template $project_templateDetails, Project_summary $project_summary){

        $this->quotation = $quotation;
        $this->workorder = $workorder;
        $this->categories = $categories;
        $this->project_templateDetails = $project_templateDetails;
        $this->project_summary = $project_summary;
        $this->middleware('auth:admin', ['except' => ['customer_quote']]);
    }


//  HERE ARE LOADS OF FUNCTIONS


 public function save_quote(Request $request)

// HERE IS LOADS OF CODE. 

        {
        if( $email == 'email'){
                Mail::to($customer['email1'])->send(new SendCustomerQuote($this-                            >save_quote($ref)));
            }

    }

}

Snapey
Snapey
1 month ago (993,535 XP)

here in your mailable

public function __construct(QuotationController $quotation)
    {
        $this->quote = $quotation;
    }

you are saying that the mailable MUST be passed an instance of QuotationController

YOU decide what you want as the parameters for the mailable. If you only want a string then remove the reference to QuotationController and just pass $ref

Its up to you.

jimmck
jimmck
1 month ago (62,225 XP)

Yeah no need for routes. URL cannot pass class instances! Controllers cannot receive class instances directly from a URL. Use Middleware to inspect a URL from a ROUTE and maybe construct a class instance and PASS it to the controller. Hence the word Middleware. Or if you are more adventurous your code Infer the Class and constructor data in the URL but really why?

Emails have separate URL links than webpages. Different mediums. Emails contains contain more static and/or time dependent data.

Snapey
Snapey
1 month ago (993,535 XP)

@jimmck stop keep going on about urls - you are just confusing things. We have not got to handling links in emails yet. This question is JUST about successfully calling the mailable and sending the email.

Please sign in or create an account to participate in this conversation.