Setting up Laravel-excel and PhpSpreadsheet macro - laravel

I am using Maatwebsite Laravel-excel version 3.1. And I want to set the default styling of the sheet. I have read the documentation about including a macro in your laravel app by setting this in my AppServiceProvider boot() method. :
Sheet::macro('getDefaultStyle',function(Sheet $sheet){
$sheet->getDefaultStyle();
});
But when everytime i reload the page it crashes the page and the laravel server in my cmd stops and re run. Here is my Export.php looks like:
public function registerEvents():array
{
return[
AfterSheet::class=>function(AfterSheet $event){
$header_style_array = [
'font'=>['bold'=>true]
];
$style_array = [
'font'=>['bold'=>true]
];
$event->sheet->getStyle('A1:B1')->getAlignment()->setHorizontal('center');
$event->sheet->getStyle('A1:B1')->applyFromArray($header_style_array);
$event->sheet->getDefaultStyle()->getFont()->setSize(5);
}];
}
I already included use Maatwebsite\Excel\Concerns\WithEvents; use Maatwebsite\Excel\Events\AfterSheet; above my Export.php file.
Is there something that I missed? I find this so hard to set up. And there's little article about setting this up.
Any help would be much appreciated
Refs: https://phpspreadsheet.readthedocs.io/en/latest/topics/recipes/#styles
https://docs.laravel-excel.com/3.1/exports/extending.html

If you examine the documentation for PhpSpreadsheet, I think you will find that the getDefaultStyle() method is not accessible from the active sheet.
To Laravel Excel, $event->sheet is equivalent to $spreadsheet->getActiveSheet(). This is why your current configuration will not work.
// this doesn't work
// $spreadsheet->getActiveSheet()->getDefaultStyle()->getFont()->setSize(5);
// this does
$spreadsheet->getDefaultStyle()->getFont()->setSize(5);
You should set default styles through the writer in BeforeWriting.
public function registerEvents():array
{
return [
BeforeWriting::class=>function(BeforeWriting $event){
$event->writer->getDefaultStyle()->getFont()->setSize(5);
},
];
}
If you want to turn this into a macro, you should use a Writer macro rather than a Sheet macro.
https://docs.laravel-excel.com/3.1/exports/extending.html#writer
public function registerEvents():array
{
Writer::macro('setDefaultStyle', function (Writer $writer) {
$writer->getDefaultStyle()->getFont()->setSize(5);
});
return [
BeforeWriting::class=>function(BeforeWriting $event){
$event->writer->setDefaultStyle();
},
];
}

Related

Laravel Nova Card Options Missing Function

I was recently given a Laravel app that uses Nova and Vue in it - all three of which I've never worked with before, so apologies in advance if I ask something that should be obvious. Inside the app there is a custom card that was already setup by the previous developer that works fine and I'm just trying to add an extra property for the Vue side to read. The structure of the card is pretty basic:
public $width = 'full';
public function currentUser()
{
return $this->withMeta(['currentUser' => Auth()->user()->id]);
}
public function is_active()
{
return $this->withMeta(['is_active' => Auth()->user()->is_active]);
}
public function information()
{
$information_id = Information::where('user_id', Auth()->user()->id)->first()->id;
return $this->withMeta(['information' => $information_id]);
}
public function component()
{
return 'overview';
}
This card shows up on the dashboard and using the Network tab in debug tools, I can see where the JSON response that goes to Vue when the card's API component is hit. The problem is, the one property I'm trying to add (information) doesn't show up. The full response is:
{
"label": "Dashboard",
"cards": [
{
"width": "full",
"component": "overview",
"prefixComponent": false,
"onlyOnDetail": false,
"currentUser": 780,
"is_active": 1
}
]
}
I've tried running npm install, npm dev and php artisan nova:publish - all are successful, but that JSON isn't picking up the new functions I added to the card's PHP file. For the sake of just trying, I even added these two:
public function testing()
{
return 'test';
}
/**
* Indicates that the analytics should show current visitors.
*
* #return $this
*/
public function currentVisitors()
{
return $this->withMeta(['currentVisitors' => true]);
}
Despite recompiling everything, those functions also don't seem to do anything - the JSON returned stays the same. I'm positive I'm doing something very basic wrong, but for the life of me can't quite figure it out.
Any suggestions?
I personally did create 2 components for Laravel Nova 3.x (one of them using VueJS), and what I did was:
Go inside the component root folder and run npm run prod (or dev ir you are testing)
Then, you must have your component registered as a composer package, so when you load the Component's ServiceProvider, you should have:
public function boot(): void
{
$this->loadViewsFrom(__DIR__.'/../resources/views', 'component-view-name');
Nova::serving(function (ServingNova $event) {
Nova::script('component-view-name', __DIR__.'/../dist/js/field.js');
});
}
That Nova::serving is the one "attaching" or sharing your desired *.js compiled file.
I am not sure about cards. My component was just a new field, not a card, but should be exactly the same.

Silverstripe 4 - SiteConfig module Image not working in template

I can't seem to get Silverstripe 4 to display images included in SiteConfig in my templates at all.I used to be able to just doe something like $SiteConfig.Logo and it would print out a automatic tag.
CustomSiteConfig:
<?php
use SilverStripe\Forms\FieldList;
use SilverStripe\ORM\DataExtension;
use SilverStripe\Forms\TextField;
use SilverStripe\Forms\TextareaField;
use SilverStripe\Forms\HeaderField;
use SilverStripe\AssetAdmin\Forms\UploadField;
use SilverStripe\Assets\Image;
use SilverStripe\ORM\DataObject;
use SilverStripe\CMS\Model\SiteTree;
class CustomSiteConfig extends DataExtension
{
private static $db = [
];
private static $has_one = [
'Logo' => Image::class,
'MobileLogo' => Image::class
];
private static $owns = [
'Logo',
"MobileLogo"
];
public function updateCMSFields(FieldList $fields)
{
$uploader = UploadField::create('Logo');
$uploader->setFolderName('Logo');
$uploader->getValidator()->setAllowedExtensions(['png','gif','jpeg','jpg']);
$fields->addFieldsToTab('Root.Main', [
HeaderField::create('hf2','Default logo'),
$uploader
]);
$uploader2 = UploadField::create('MobileLogo');
$uploader2->setFolderName('MobileLogo');
$uploader2->getValidator()->setAllowedExtensions(['png','gif','jpeg','jpg']);
$fields->addFieldsToTab('Root.Main', [
HeaderField::create('hf3','Mobile Logo'),
$uploader2
]);
}
}
But when I try in my template file. I get no URL
$SiteConfig.Logo
or
$SiteConfig.Logo().Link
etc
Nothing works?
A few things to check:
Verify that $SiteConfig is available as variable at that point in your template (Try using $SiteConfig.Title)
Verify that the extension is actually added to SiteConfig (do you see the CMS Fields?)
Did you add $owns later? run ?flush=1 again and re-save the SiteConfig *
Verify that both the SiteConfig and the File is published. (Save & Publish the SiteConfig twice, then check in the file manager if the file is published) **
[*] $owns is just a directive that when SiteConfig->doPublish() is called, it will also publish all files
[**] I've seen a bug that DataObjects don't actually publish files sometimes. Saving twice might work.
Just like Zauberfisch said, your image is probably not published. However, publishing the image after writing the owner can be tricky.
I usually through in this code
public function onAfterWrite()
{
parent::onAfterWrite();
if ( $this->LogoID ) {
$this->Logo()->doPublish();
}
if ( $this->MobileLogoID ) {
$this-> MobileLogo()->doPublish();
}
}
It's messy, I know, but it can save you a couple of hours. After saving you can remove it as the $owns hook will start to kick-in to all newly created objects.
We can Use This one
$SiteConfig.Logo.URL

Laravel maatwebsite/excel (PhpSpreadsheet) and TCPDF wrong sheet orientation

I have a Laravel application using the maatwebsite/excel package to generate and excel file. I also allow the user to download that excel file as a PDF.
The problem is that when the pdf is generated, the pagination is vertical and the content gets cut.
I have the excel file set to landscape but the pagination is still vertical. I can not find in the docs a way to set the pdf direction
public function registerEvents(): array
{
return [
AfterSheet::class => function (AfterSheet $event) {
// Landscope orientation
$event->sheet->getDelegate()->getPageSetup()->setOrientation(PageSetup::ORIENTATION_LANDSCAPE);
}
];
}
I'm exporting the PDF file using the TCPDF library. I've seen a parameter in the docs to set as landscape but I can not figure out the way to pass the parameter to the TCPDF constructor with the maatwebsite/excel api. This is the syntax:
return (new GanttExport)->download('gantt_' . time() . '.pdf', Excel::TCPDF);
Have you tried using BeforeSheet event? It does work in my code. And dont forget to implement Maatwebsite\Excel\Concerns\WithEvents interface within the class.
public function registerEvents(): array
{
return [
BeforeSheet::class => function (BeforeSheet $event) {
$event->sheet
->getPageSetup()
->setOrientation(PageSetup::ORIENTATION_LANDSCAPE);
}
];
}

Why does Laravel dd only show two levels?

When I use dd($myVar) in Laravel the output only shows two levels. Why is that?
The first level is shown expanded, and I can click to expand the second level. The third level is simply summarised with something like array:2 (in the case of any array with two elements).
EDIT:
I noticed that the output of var_dump() was similarly limited. I changed the xdebug depth setting to 10, ini_set('xdebug.var_display_max_depth', 10), which fixed the problem in var_dump, but not dd.
The easiest way is to use ctrl+click to expand all children, you can even search values and variables using ctrl+F
If it doesn't suit you, you can create your own helper that depends on xdebug configuration settings. Add a new file "helpers.php" then autoload it using composer.json
"autoload": {
"files": [
"app/helpers.php"
]
}
I used an anonymous class, you may create a dedicated class to cleanup/reuse code
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Illuminate\Support\Debug\HtmlDumper;
function xdebug_dd(...$args)
{
http_response_code(500);
$obj = new class {
public function dump($value)
{
if (class_exists(CliDumper::class)) {
$dumper = new HtmlDumper;
$dumper->dump((new VarCloner)->cloneVar($value), null, [
'maxDepth' => ini_get('xdebug.var_display_max_depth')
]);
} else {
var_dump($value);
}
}
};
foreach ($args as $x) {
$obj->dump($x);
}
die(1);
}

How to extend laravel 4 core?

I am a newb learning laravel 4. I want to override specific helper functions. Or add new functions to Url, Str etc. How to do this?
Depending on what part of Laravel you want to extend or replace, there are different approaches.
Macros
Adding functions to Str is really easy, because of "macros":
Here's a short example for adding function:
Str::macro('test', function($str) {
return 'Testing: ' . $str . '!';
});
You can then call this function as expected:
echo Str::test('text'); // Outputs "Testing: text!"
Adding functions using macros are supported by Str, Form, HTML and Response.
IOC Based Extension
To extend URL one must use the IOC of Laravel. This is explained in the docs (as mentioned by BenjaminRH). I agree it can be a bit hard to understand. Here's a step-by-step to extend URL:
Create folder app/lib to hold your extension classes.
Add this folder to autoloading:
In app/start/global.php, append the lib path to the class loader:
ClassLoader::addDirectories(array(
app_path().'/commands',
app_path().'/controllers',
app_path().'/models',
app_path().'/database/seeds',
app_path().'/lib'
));
Then add the path to composer.json classmap:
"autoload": {
"classmap": [
"app/commands",
"app/controllers",
"app/models",
"app/database/migrations",
"app/database/seeds",
"app/tests/TestCase.php",
"app/lib"
]
},
Create the custom UrlGenerator app/lib/CustomUrlGenerator.php:
<?php
use \Illuminate\Routing\UrlGenerator;
class CustomUrlGenerator extends UrlGenerator
{
public function test()
{
return $this->to('/test');
}
}
Create a service provider app/lib/CustomRoutingServiceProvider.php:
<?php
use \Illuminate\Routing\RoutingServiceProvider;
class CustomRoutingServiceProvider extends RoutingServiceProvider
{
public function boot()
{
App::bind('url', function()
{
return new CustomUrlGenerator(
App::make('router')->getRoutes(),
App::make('request')
);
});
parent::boot();
}
}
Register the service provider in app/config/app.php:
Add CustomRoutingServiceProvider to the providers array.
For example, right after the Workbench provider:
'Illuminate\Workbench\WorkbenchServiceProvider',
'CustomRoutingServiceProvider',
Run composer dump-autoload from project root folder.
Done. Use like:
URL::test();
NOTE The code is tested, but may contain some errors
Interesting that you should mention this, actually. A whole documentation section was just recently added, which covers this in detail. It's very clear, and easy to understand. If you've been using Laravel at all, it might not even surprise you that Laravel actually provides an extend method for a lot of core components.
Following Fnatte's answer, today's versions of Laravel do some extra processing in the url binding. Redefining the whole binding is no longer a practical option.
Here is how I ended up for extending the URL facade.
First, create your child class using this boilerplate:
use Illuminate\Routing\UrlGenerator;
class YourUrlGenerator extends UrlGenerator {
public function __construct(UrlGenerator $url)
{
parent::__construct($url->routes, $url->request);
}
// redefine or add new methods here
}
Then, add this in a ServiceProvider:
$url = $this->app['url'];
$this->app->singleton('url', function() use ($url)
{
return new YourUrlGenerator($url);
});
The point is simply that the original url binding should be executed at least once before we override it with our own.

Resources