Link to home
Start Free TrialLog in
Avatar of zack tim
zack timFlag for Morocco

asked on

How to implement hide or display price when printing voucher to PDF

Hi Experts,

I have a create-voucher model and I would like to add the possibility to display and hide the price of the room on the voucher when printed only


My Model:

https://gitlab.com/webdev866/vouchermanager/-/blob/master/app/CreateVoucher.php


View:

https://gitlab.com/webdev866/vouchermanager/-/blob/master/resources/views/admin/createVouchers/create.blade.php


What is the best way to achieve this?

Avatar of Chris Stanyon
Chris Stanyon
Flag of United Kingdom of Great Britain and Northern Ireland image

Hey Zack,

Not entirely clear what you're asking. When you create a PDF, you use a View, so if you want to show the Price, just add it to the View !!

Avatar of zack tim

ASKER

Hi Chris,


Right, however if I want to hide it, it's what I'm trying to achieve, like a selection menu, Hide Price (Yes/No)

Ahh right !!

Few ways to do it - basically, you'll need a way to pass a value into your Controller. You could do this with a simple radio and a form POST:

<form method="post" action="some_route">
  <input type="radio" name="showprice" value="yes" selected>
  <input type="radio" name="showprice" value="no">
  <input type="submit" value="Get PDF">
</form>

Open in new window

Then, in your Controller, just read in $request->showprice and pass it into your View:

$showprice = $request->showprice == "yes";
$pdf = PDF::loadView('admin.vouchersAccountings.get_single_pdf', compact('voucher', 'showprice'));

Open in new window

You can see we're passing showprice into the view as a Boolean, so now in your view you can do;:

@if ($showprice)
  // show the price data here :)
@endif

Open in new window

That's great route, I will do my best to implement it by myself, just a question, do we need to store showprice in database in case we wanted to re-print it?

Hmmm - that will depend on your own business logic. If you want it to persist, then yeah - you'd want to store it in the DB (assuming you want to set the value on a per-voucher basis)

Ok I have added show_price to the create_vouchers table


<?php
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class UpdateVouchersTable extends Migration {     /**      * Run the migrations.      *      * @return void      */     public function up()     {         Schema::table('create_vouchers', function (Blueprint $table) {             $table->string('show_price')->after('total_amount');         });     }     /**      * Reverse the migrations.      *      * @return void      */     public function down()     {         Schema::table('create_vouchers', function (Blueprint $table) {             $table->dropColumn('show_price');         });     } }

Open in new window


User generated image


Model:

 public const SHOWPRICE_RADIO = [
        'yes' => 'Yes',
        'no'  => 'No',
    ];

Open in new window

then I added showprice in every possible view:

 <div class="form-group {{ $errors->has('showprice') ? 'has-error' : '' }}">
                            <label class="required">{{ trans('cruds.createVoucher.fields.showprice') }}</label>
                            @foreach(App\CreateVoucher::SHOWPRICE_RADIO as $key => $label)
                                <div>
                                    <input type="radio" id="showprice_{{ $key }}" name="showprice" value="{{ $key }}" {{ old('showprice', '') === (string) $key ? 'checked' : '' }} required>
                                    <label for="showprice_{{ $key }}" style="font-weight: 400">{{ $label }}</label>
                                </div>
                            @endforeach
                            @if($errors->has('showprice'))
                                <span class="help-block" role="alert">{{ $errors->first('showprice') }}</span>
                            @endif
                            <span class="help-block">{{ trans('cruds.createVoucher.fields.showprice_helper') }}</span>
                        </div>

Open in new window

I get this error while trying to create a voucher for testing:


User generated image


OK,

First off, I'd suggest you use a Boolean for your show_price column. Using magic strings such as Yes/No only leads to problems down the line, whereas a true/false removes ambiguity and any chance of errors. Set your array to 1 and 0 instead of yes/no:

public const SHOWPRICE_RADIO = [
    0 => 'No',
    1 => 'Yes',
];

Open in new window

and  then add it to the $casts array in your model:

protected $casts = [
    'show_price' => 'boolean',
];

Open in new window

Because you're casting the property to boolean, it means that you can easily use it your logic / blade:

if ($voucher->show_price) {
   //
}

Open in new window

The error you're getting is exactly as it says - you haven't set a default value for the show_price field, and you can't leave it null (it's not nullable), so you MUST provide a value when you create a new record. Now, in your form you've named the radio showprice, but the column in your DB is called show_price, so the 2 won't match up when you call create(). Keep the names the same as your DB column. You must also add the show_price column to the $fillable array on your Model otherwise you can't mass to assign it.

You are correct Chris, I changed show_price column to showprice and it worked, but I had to do it manually I was looking for a quick fix.


Where that $cast came from? I don't have it in the Model but I added it


if ($voucher->show_price) {   <== This line means True by default?
   //
}

Open in new window


Ok I'm at the final step, should I create a method in controller that takes me to a new view with removed price if I select No ?

Hey Zack,

Yeah - manually changing the column is the quick and easy way - just make sure you update your previous migration as well. You're not going to re-run it, but in the future if you need to rebuild the DB, you'll want it to be correct.

The $casts property allows you to tell your Model that certain columns should be treated as certain types, and if it doesn't exists, then you're right to add it. For example, if you have a DateOfBirth column, you would want to cast that to a Date. MySQL doesn't actually have a Boolean data type, so it stores the values as 0 and 1 (tinyint). By adding the property to $casts[] means that when we read and write to the database, the values of 0 and 1 are converted to true and false automatically.

if ($voucher->show_price) {

Yeah - that basically says "if show_price === true"

The opposite would be:

if ( ! $voucher->show_price) {

which is "if show_price === false"

You don't need a new view to display or hide the price - you've now got the show_price (boolean), so just add the logic into your view. Something like this maybe ??

@if ($voucher->showprice)
  <h4>Price</h4>
  <p>{{ $voucher->total_amount }}</p>
@endif

Open in new window




Maybe I've done something wrong


my controller:

 public function get_single_pdf(CreateVoucher $voucher, Request $request)
{
    $showprice = $request->showprice == "yes";
    $voucher->loadMissing(['hotel_name', 'agent', 'room_type']);
    $pdf = PDF::loadView('admin.vouchersAccountings.get_single_pdf', compact('voucher','showprice'));
    return $pdf->download('voucher.pdf');
}

Open in new window

View

  <tr>
                                    <th>
                                        {{ trans('cruds.createVoucher.fields.total_amount') }}
                                    </th>
                                    @if (!$voucher->showprice)
                                         <h4>Price</h4>
                                        <p>{{ $voucher->total_amount }}</p>
                                    @endif
                                </tr>
                                <tr>

Open in new window


total is displaying


Can we simply remove the total amount and Rate Per Night

User generated image


OK,

So $request->showprice only exists when you POST that value and you only need to do that when you're updating or creating a new Voucher. Once you've created the Voucher in the DB, then you access the showprice property just like any other. In your Controller all you need is this:

public function get_single_pdf(CreateVoucher $voucher, Request $request)
{
    $voucher->loadMissing(['hotel_name', 'agent', 'room_type']);
    $pdf = PDF::loadView('admin.vouchersAccountings.get_single_pdf', compact('voucher'));
    return $pdf->download('voucher.pdf');
}

Open in new window

You're passing the $voucher into the view, and the $voucher has a showprice property, so in your view you just access it like any other property.

Also, in your view code above, you have this:

@if (!$voucher->showprice)

The Exclamation mark means NOT, so that line says "if showprice is false (NOT true)". Drop the exclamation mark (!) :

@if ($voucher->showprice)

If you want to hide the total price, then just wrap it in the @if ($voucher->showprice) check:

@if ($voucher->showprice)
<tr>
    <th>
        {{ trans('cruds.createVoucher.fields.total_amount') }}
    </th>
    <td>
        {{ $voucher->total_amount }}
    </td>
</tr>
@endif

Open in new window

Ahhh - I just realised you're still using magic strings for your values, so you'll need to do some extra work, you can't just check true / false - you actually need to check the value:

@if ($voucher->showprice == "yes")

This is why we use Booleans and not strings !!

Okay, I'm still facing issues


I got an error

ErrorException 
   Undefined variable: voucher (View: /var/www/vouchermanager/resources/views/layouts/print.blade.php)
 
           http://vouchermanager.test/admin/create-vouchers/getpdf/7        
 
Hide solutions
 


 
                       Possible typo $voucher                    
 
Did you mean $createVoucher?

Open in new window

changing $voucher to $createVoucher display the PDF still with total_amount showing up.


Then again when I try to re-generate the PDF I got a different error:

ErrorException 
   Undefined variable: createVoucher (View: /var/www/vouchermanager/resources/views/admin/vouchersAccountings/get_single_pdf.blade.php)
 
           http://vouchermanager.test/admin/vouchers-accountings/getpdf/7        
 
Hide solutions
 


 
                       Possible typo $createVoucher                    
 
Did you mean $voucher?

Open in new window


the get_single_pdf blade is having a fight with print blade :)


I guess there is a simpler way, if you select ShowPrice = No, then it takes you to another view with pricing <tr> removed, if it's yes then no change What do you think?


OK,

I definitely wouldn't have 2 different views - you're just doubling up your code which goes against best practice.

I don't know what state your code is in at the moment - if I look at your GitLab repo, it seems like you have a print.blade file which is a full HTML document, and then you have a get_single_pdf.blade file, which is a partial template. That partial extends print, but print doesn't look extendable, so somethings wrong. Push all your changes to your repo and then I'll take a look.
Hey Zack,

First off, you've still got a disconnect on your showprice field. You're casting show_price (underscore) but in $fillable, you've added showprice (no underscore). The field in your Database is called showprice, so go with that everywhere.

The field in your DB is still an string, according to the migration, so you might want to chagne that to a boolean. If you do that, then drop the quotes around 0 and 1 in your SHOWPRICE_RADIO array.

Now - onto your PDF. You're still getting this all quite wrong !! In your CreateVoucherController, you have a method called getpdf. Now in that method, you're loading up your voucher and storing it in a variable called $createVoucher - you then pass this into the createVouchers/getpdf view - all good so far.

Now, this is where it starts to go wrong. If you take a look at your getpdf view, you'll see that it uses the $createVoucher variable, as it should, but nowhere in there do you do the showprice check. Because your voucher is in the $createVoucher variable, then you'd need $createVoucher->showprice.

However, if you take a look at the print.blade.php template, that is a full HTML page, with no way of extending it and that uses a couple of variables - one called $voucher and one called $voucherId which NEVER get passed into the view. Because it's not extendable, your getpdf.blade view NEVER get's rendered. We've gone through this before and I though we had it sorted. Your base template (print.blade in your case), should NOT have data in it - it should however have a @yield('content') line so that the child template (getpdf.blade in your case) knows where to get rendered.

Thank you Chris for your help, you are a real troubleshooter :)


First off, you've still got a disconnect on your showprice field. You're casting show_price (underscore) but in $fillable, you've added showprice (no underscore). The field in your Database is called showprice, so go with that everywhere.


=> Right, I've corrected it.


The field in your DB is still an string, according to the migration, so you might want to chagne that to a boolean. If you do that, then drop the quotes around 0 and 1 in your SHOWPRICE_RADIO array.


=> I've corrected it manually, however can you confirm if this is the right syntaxe


public function up()
    {         Schema::table('create_vouchers', function (Blueprint $table) {             $table->boolean('showprice')->after('total_amount');         });     }

Open in new window


Now - onto your PDF. You're still getting this all quite wrong !! In your CreateVoucherController, you have a method called getpdf. Now in that method, you're loading up your voucher and storing it in a variable called $createVoucher - you then pass this into the createVouchers/getpdf view - all good so far.

Now, this is where it starts to go wrong. If you take a look at your getpdf view, you'll see that it uses the $createVoucher variable, as it should, but nowhere in there do you do the showprice check. Because your voucher is in the $createVoucher variable, then you'd need $createVoucher->showprice.


=> This is where I lost it, putting @yield content in print.blade.php obviously rendered the view with no css

User generated image

What I did is something different, I guess you won't like it, I created different blades, I left print.blade.php as is with HTML and I make it to render createvoucher 

Then I created another print_get_single_pdf.blade.php to render single_pdf and I put $voucher all in.


Result=> it worked out just fine. Vouchers with ShowPrice set to No, doesn't show the pricing as expected.


what do you think.


I'm opening another question because when I select a range in Voucher Accounting if there is no data, it still output a random total balance instead of zero, not sure if it has to do something with


$total_balance = $vouchers->sum('total_amount');

ASKER CERTIFIED SOLUTION
Avatar of Chris Stanyon
Chris Stanyon
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial

Thank you so much Chris, love you man :)