I am using dompdf in my Laravel v8.26.1 app. I am also using Laravel Livewire v1.3.5.
I can get a view to download a pdf outside of Livewire, but can't seem to get it to work inside a Livewire view. I've tried every configuration I could think of, but keep running into road blocks. I am either getting an Undefined variable: workorder error, or no error at all, but also no download.
route
...
Route::livewire('/workorder/{workorder}/print', 'admin.workorders.printer')->name('admin.workorder.print')->layout('layouts.admin');
...
livewire/admin/workorders/print.blade
...
<div>
<button wire:click="generatePDF({{$workorder->id}})">Create PDF</button>
</div>
...
livewire/admin/workorders/printer.php
...
use Barryvdh\DomPDF\Facade as PDF;
...
public function generatePDF($id)
{
$record = Workorder::find($id);
$data = [
'project_id' => $record->project_id,
'project_name' => $record->project_name,
'customer_id' => $record->customer_id,
'generator_id' => $record->generator_id,
'workorder_po_number' => $record->workorder_po_number,
'workorder_number' => $record->workorder_number,
'prevailing_wage' => $record->prevailing_wage,
];
$pdf = PDF::loadView('livewire.admin.workorders.print', $data);
return $pdf->download('demo.pdf');
}
Here you go, just found this. fixed me right up. The only thing I had to change was in the $viewData variable, i just had to make it an array - ['viewData' => $viewData] and then i could access it in my view.
Here is the code in case that link ever goes down:
$pdfContent = PDF::loadView('view', $viewData)->output();
return response()->streamDownload(
fn () => print($pdfContent),
"filename.pdf"
);
Related
I was making PDF from my webpage, but there are few styling which i am unable to load, like display: flex.
So for troubleshoot and go with other approach i decided to fetch first html from views.
my PDF were loading html from view with following code.
my view url is: https://digital.xtrazcon.com/reports/17
$report = Report::where('id', '=', $id)->firstOrFail();
$pdf = PDF::loadView('reports.container', ['view' => 'show', 'report' => $report, 'pdf_view' => 'yes'])->save($report_path);
so for calling rendered html, i tried following codes; but on all i am getting error.
all code after die are actually my tries.
$html = view('reports.container', compact(['view' => 'show', 'report' => $id])->render();
die(var_dump($html));
Says: compact(): Undefined variable $show
$html = view('reports.container', compact(['view' => 'show', 'report' => $report])->render();
die(var_dump($html));
Says: compact(): Undefined variable $show
Following one loads fine, but on view it gives error of missing data in the view which is 17
$html = view('reports.container', compact('report'))->render();
die(var_dump($html));
Says: Undefined variable $view (View: /var/www/vhosts/digital.xtrazcon.com/httpdocs/resources/views/reports/container.blade.php)
Line of code, which gives the above error is below
<?php echo $__env->make('reports.' . $view, \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?>
Please help, how can i call rendered html, so i can fix it for PDF.
Update Made
I tried something similar advise here, Laravel Meaning of Load View function
I have my view path at
/httpdocs/resources/views/reports/show.blade.php
so i decided to use
reports/show as a view
I tried following updated code then
$report = Report::where('id', '=', $id);
$html = view('reports/show', compact('report'));
dd($html->render());
die();
But still getting following error
Property [host] does not exist on the Eloquent builder instance. (View: /var/www/vhosts/digital.xtrazcon.com/httpdocs/resources/views/reports/show.blade.php)
I got that, the previous explanation mentioned at following query is similar and helpful to understand the direction.
Laravel Meaning of Load View function
i was just missing firstOrFail()
so i added this line, to call report first.
$report = Report::where('id', '=', $id)->firstOrFail();
then i call view in a following way, by adding my parameter in an array.
$html = view('reports/show',['report'=>$report]);
dd($html->render());
If i want to use compact, i tried following and works
$html = view('reports/show',compact('report'));
I have been trying to get cakephp to suggest input from data that is from my tables like autocomplete. I've done some reading about how some other people have done this but still can't figure it out. Currently it seems that every time my controller is waiting for an ajax request and it is always false. No errors come up from the console some i'm not sure what i'm doing wrong. I tried removing the if ($this->request->is('ajax')) statement but then I get a error about it cannot emit headers.
Here is my search function in InvoicesController which I have taken code from someone else example but failed to implement it.
public function search()
{
if ($this->request->is('ajax')) {
$this->autoRender = false;
pr('b');
$name = $this->request->query['term'];
$results = $this->Invoices->find('all', [
'conditions' => [ 'OR' => [
'id LIKE' => $id . '%',
]]
]);
$resultsArr = [];
foreach ($results as $result) {
$resultsArr[] =['label' => $result['full_name'], 'value' => $result['id']];
}
echo json_encode($resultsArr);
}
}
And here is my search.ctp
<?php use Cake\Routing\Router; ?>
<?php echo $this->Form->input('id', ['type' => 'text']);?>
<script>
jQuery('#id').autocomplete({
source:'<?php echo Router::url(array('controller' => 'Invoices', 'action' => 'search')); ?>',
minLength: 1
});
</script>
This is my invoice table and the ids are what I want to be suggested from what users type in.
I may not be seeing your exact problem but let me point out a few things I see that might help this issue.
Remove this line. It is not necessary
$this->autoRender = false;
Instead you should be doing this at the end. See using the RequestHandler
$this->set('resultsArr', $resultsArr);
// This line is what handles converting your array into json
// To get this to work you must load the request handler
$this->set('_serialize', 'resultsArr');
This will return the data without a root key
[
{"label":"Label Value"},
{"label":"Another Label Value"}
]
Or you can do it like this
$this->set('_serialize', ['resultsArr']);
This will return data like
{"resultArr":[
{"label":"Label Value"},
{"label":"Another Value"}
]}
Replace your finder query with this.
$resultArr = $this->Invoices->find('all')
->where(['id LIKE' => $id . '%'])
// If you want to remap your data use map
// All queries are collections
->map(function ($invoice) {
return ['label' => $invoice->full_name, 'id' => $invoice->id];
});
It seems to me you might want to review the new cakephp 3 orm. A lot of hard work went into writing these docs so that they could be easily read and relevant. I'm not one to push docs on people but it will save you hours of frustration.
Cakephp 3 ORM documentation
A few minor things I noticed that are also problems.
You never define $id.
You define $name but never use it.
pr is a debug statement and I am not sure why you have it.
Based on your comment, here is an update on ajax detection.
// By default the ajax detection is limited to the x-request-with header
// I didn't want to have to set that for every ajax request
// So I overrode that with the accepts header.
// Any request where Accept is application/json the system will assume it is an ajax request
$this->request->addDetector('ajax', function ($request) {
$acceptHeaders = explode(',', $request->env('HTTP_ACCEPT'));
return in_array('application/json', $acceptHeaders);
});
I am creating some hard coded routes that will likely be changed again freely in the future. To abstract the ideas a bit:
We have a controller/method BuySubscriptionController#start:
class BuySubscriptionController
function start()
{
$plan = Plan::findBySlug($request->get('plan'));
return view('someView', ['plan' => $plan]);
}
}
We currently have the following route:
Route::get('/buy-subscription/start', 'BuySubscriptionController#start');
This means the sales team would need to advertise the following urls:
site.com/buy-subscription/start?plan=plan-one
site.com/buy-subscription/start?plan=plan-two
Now we have been requested to have a few specialized routes:
site.com/purchase/the-basic-plan (plan-one)
site.com/purchase/the-mega-plan (plan-two)
Now I am trying to add these specialized urls to my routes. I was hoping to do something as follows, but does not work:
Route::get('/purchase/the-basic-plan', [
'uses' => 'BuySubscriptionController#start',
'with' => ['plan' => 'plan-one']
]);
Route::get('/purchase/the-mega-plan', [
'uses' => 'BuySubscriptionController#start',
'with' => ['plan' => 'plan-two']
]);
Is there any way to achieve this, simply, without over engineering some new translation layer? Keep in mind that next week the url might be /buy/the-god-plan meaning plan-one, so being able to simple add a line to my routes seems ideal.
You can define a route that takes the plan as a parameter, and use Regular Expression Constraints so that parameter can only take certain values that you allow. So your route definition can look like this:
Route::get('/purchase/{plan}', 'BuySubscriptionController#start')
->name('purchase-plan')
->where('plan', 'the-basic-plan|the-mega-plan');
Then in your controller action just use the parameter:
class BuySubscriptionController
{
protected $plans = [
'the-basic-plan' => 'plan-one',
'the-mega-plan' => 'plan-two'
];
function start($plan)
{
// You can use an associative array to convert the $plan parameter
// into the value you need for querying the database
$plan = Plan::findBySlug($this->plans[$plan]);
return view('someView', ['plan' => $plan]);
}
}
If you need to generate URLs for the route you can just use the route helper method and pass it the plan name:
route('purchase-plan', 'the-basic-plan');
And you'll get:
site.com/purchase/the-basic-plan
This solution allows you to add any number of plan names by just adding the public plan for the URL in the where constraint of the route, then associating that value with the one you need for the query in your controller's $plans property.
I just started working with Laravel. I need to rewrite a whole system I made some years ago, using Laravel 4 as base framework. In my old system, I used to have a constant.php file with some constants declared, and a globals.php file which contained lots of array sets (for example, categories statuses, type of events, langs, etc.). By doing so, I could use something like
foreach ( $langs as $code => $domain ) {
// Some stuff
}
anywhere in my app.
My question is, how can I store that info in the so called "laravel way". I tried using some sort of object to store this info, setting this as a service and creating for it a facade:
app/libraries/Project/Constants.php
namespace PJ;
class Constants {
public static $langs = [
'es' => 'www.domain.es',
'en' => 'www.domain.us',
'uk' => 'www.domain.uk',
'br' => 'www.domain.br',
'it' => 'www.domain.it',
'de' => 'www.domain.de',
'fr' => 'www.domain.fr'
];
}
app/libraries/Project/ConstantsServiceProvider.php
namespace PJ;
use Illuminate\Support\ServiceProvider;
class ConstantsServiceProvider extends ServiceProvider {
public function register() {
$this->app->singleton('PJConstants', function() {
return new Constants;
});
}
}
app/libraries/Project/ConstantsFacade.php
namespace PJ;
use Illuminate\Support\Facades\Facade;
class ConstantsFacade extends Facade {
protected static function getFacadeAccessor() {
return 'PJConstants';
}
}
composer.json
"psr-4": {
"PJ\\": "app/libraries/Project"
},
and so I access that property as PJ\Constants::$langs.
This works, but I doubt it is the most efficient or correct way of doing it. I mean, is it the right way to "propagate" a variable by creating a whole Service Provider and facades and all such stuff? Or where should I put this data?
Thanks for any advice.
EDIT # 01
Data I want to pass to all controllers and views can be directly set in script, like in the example at the beginning of my post, but it can also be generated dynamically, from a database for example. This data could be a list of categories. I need them in all views to generate a navigation bar, but I also need them to define some routing patterns (like /category/subcategory/product), and also to parse some info in several controllers (Like get info from the category that holds X product).
My array is something like:
$categories = [
1 => ['name' => 'General', 'parent' => 0, 'description' => 'Lorem ipsum...'],
2 => ['name' => 'Nature', 'parent' => 0, 'description' => 'Lorem ipsum...'],
3 => ['name' => 'World', 'parent' => 0, 'description' => 'Lorem ipsum...'],
4 => ['name' => 'Animals', 'parent' => 2, 'description' => 'Lorem ipsum...']
]
Just as an example. Index is the id of the category, and the Value is info associated with the category.
I need this array, also, available in all Controllers and Views.
So, should I save it as a Config variable? How else could I store these data; what would be the best and semantically correct way?
For most constants used globally across the application, storing them in config files is sufficient. It is also pretty simple
Create a new file in the app/config directory. Let's call it constants.php
In there you have to return an array of config values.
return [
'langs' => [
'es' => 'www.domain.es',
'en' => 'www.domain.us'
// etc
]
];
And you can access them as follows
Config::get('constants.langs');
// or if you want a specific one
Config::get('constants.langs.en');
And you can set them as well
Config::set('foo.bar', 'test');
Note that the values you set will not persist. They are only available for the current request.
Update
The config is probably not the right place to store information generated from the database. You could just use an Eloquent Model like:
class Category extends Eloquent {
// db table 'categories' will be assumed
}
And query all categories
Category::all();
If the whole Model thing for some reason isn't working out you can start thinking about creating your own class and a facade. Or you could just create a class with all static variables and methods and then use it without the facade stuff.
For Constants
Create constants.php file in the config directory:-
define('YOUR_DEFINED_CONST', 'Your defined constant value!');
return [
'your-returned-const' => 'Your returned constant value!'
];
You can use them like:-
echo YOUR_DEFINED_CONST . '<br>';
echo config('constants.your-returned-const');
For Static Arrays
Create static_arrays.php file in the config directory:-
class StaticArray
{
public static $langs = [
'es' => 'www.domain.es',
'en' => 'www.domain.us',
'uk' => 'www.domain.uk',
'br' => 'www.domain.br',
'it' => 'www.domain.it',
'de' => 'www.domain.de',
'fr' => 'www.domain.fr'
];
}
You can use it like:-
echo StaticArray::$langs['en'];
Note: Laravel includes all config files automatically, so no need of manual include :)
Create common constants file in Laravel
app/constants.php
define('YOUR_CONSTANT_VAR', 'VALUE');
//EX
define('COLOR_TWO', 'red');
composer.json
add file location at autoload in composer.json
"autoload": {
"files": [
"app/constants.php"
]
}
Before this change can take effect, you must run the following command in Terminal to regenerate Laravel’s autoload files:
composer dump-autoload
For global constants in Laravel 5, I don't like calling Config for them. I define them in Route group like this:
// global contants for all requests
Route::group(['prefix' => ''], function() {
define('USER_ROLE_ADMIN','1');
define('USER_ROLE_ACCOUNT','2');
});
I think the best way is to use localization.
Create a new file messages.php in resources/lang/en (en because that is what is set in my config/app 'locale'=>'en')
return an array of all your values
return [
'welcome' => 'Welcome to our application'
];
to retrieve for laravel 5.3 and below
echo trans('messages.welcome');
or
echo Lang::get('messages.welcome');
for 5.4 use
echo __('messages.welcome')
laravel 5.0 localization
or
laravel 5.4 localization
Just to add to the above answer you will have to include the config class before you could start using it in Laravel 5.3
use Illuminate\Support\Facades\Config;
Atleast in Laravel 5.4, in your constructor you can create them;
public function __construct()
{
\Config::set('privileged', array('user1','user2');
\Config::set('SomeOtherConstant', 'my constant');
}
Then you can call them like this in your methods;
\Config::get('privileged');
Especially useful for static methods in the Model, etc...
Reference on Laracasts.com https://laracasts.com/discuss/channels/general-discussion/class-apphttpcontrollersconfig-not-found
Just put a file constants.php file into the config directory and define your constants in that file, that file will be auto loaded,
Tested in Laravel 6+
Create a constants class:
<?php
namespace App\Support;
class Constants {
/* UNITS */
public const UNIT_METRIC = 0;
public const UNIT_IMPERIAL = 1;
public const UNIT_DEFAULT = UNIT_METRIC;
}
Then use it in your model, controller, whatever:
<?php
namespace App\Models;
use App\Support\Constants;
class Model
{
public function units()
{
return Constants::UNIT_DEFAULT;
}
}
I have a controller which I use for a login form. In the view, I have a {error} variable which I want to fill in by using the parser lib, when there is an error. I have a function index() in my controller, controlled by array $init which sets some base variables and the error message to '':
function index()
{
$init = array(
'base_url' => base_url(),
'title' => 'Login',
'error' => ''
);
$this->parser->parse('include/header', $init);
$this->parser->parse('login/index', $init);
$this->parser->parse('include/footer', $init);
}
At the end of my login script, I have the following:
if { // query successful }
else
{
$init['error'] = "fail";
$this->parser->parse('login/index', $init);
}
Now, of course this doesn't work. First of all, it only loads the index view, without header and footer, and it fails at setting the original $init['error'] to (in this case) "fail". I was trying to just call $this->index() with perhaps the array as argument, but I can't seem to figure out how I can pass a new $init['error'] which overrides the original one. Actually, while typing this, it seems to impossible to do what I want to do, as the original value will always override anything new.. since I declare it as nothing ('').
So, is there a way to get my error message in there, or not? And if so, how. If not, how would I go about getting my error message in the right spot? (my view: {error}. I've tried stuff with 'global' to bypass the variable scope but alas, this failed. Thanks a lot in advance.
$init musst be modified before generating your view.
To load your header and footer you can include the following command and the footer's equivalent into your view.
<?php $this->load->view('_header'); ?>
to display errors, you can as well use validation_errors()
if you are using the codeigniter form validation.
if you are using the datamapper orm for codeigniter you can write model validations, and if a query fails due to validation rule violation, you get a proper error message in the ->error property of your model.
Code for your model:
var $validation = array(
'user_name' => array(
'rules' => array('required', 'max_length' => 120),
'label' => 'Name'
)
);
You might try this:
function index() {
$init = array(
'base_url' => base_url(),
'title' => 'Login',
'error' => ''
);
$string = $this->parser->parse('include/header', $init, TRUE);
$string .= $this->parser->parse('login/index', $init, TRUE);
$string .= $this->parser->parse('include/footer', $init, TRUE);
$this->parser->parse_string(string);
}
In parse()you can pass TRUE (boolean) to the third parameter, when you want data returned instead of being sent (immediately) to the output class. By the other hand, the method parse_string works exactly like `parse(), only accepts a string as the first parameter in place of a view file, thus it works in conjunction.