Can pdf document generated with maatwebsite/excel contain many columns/ Horizontall scrolling? - pdf-generation

in Laravel 8 with "maatwebsite/excel": "^3.1" I try to write to pdf file document with many columns
and I try to set Orientation = LANDSCAPE with code :
class HostelsByIdExport implements FromCollection, WithEvents, WithHeadings, WithPreCalculateFormulas, WithColumnFormatting, ShouldAutoSize, WithColumnWidths, WithStyles
{
public function __construct(array $hostelsList)
{
...
}
public function collection()
{
...
}
public function registerEvents(): array
{
return [
// Handle by a closure.
BeforeExport::class => function (BeforeExport $event) {
...
},
// Array callable, refering to a static method.
BeforeWriting::class => [self::class, 'beforeWriting'],
AfterSheet::class => function (AfterSheet $event) {
$event->sheet ->getPageSetup()->setOrientation(\PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_LANDSCAPE);
}, // AfterSheet::class => function (AfterSheet $event) {
];
}
as I read in 3.1 documentations at https://docs.laravel-excel.com/3.1/exports/extending.html
and it works ok, but even landscape genarated page can not contain all columns and I see tyhat many columns are cut off.
Are there some decisions here? Maybe something like Horizontall scrolling ?
Thanks in advance!

Related

Eager Loading vs Lazy Loading with API Resource

Could you please suggest which option I should use for the following example.
I'm currently building an API to get Places, Place model contains User and Location models.
Relationships are set like that:
class Place extends Model
{
protected $guarded = [];
public function createdBy()
{
return $this->belongsTo(User::class, 'created_by', 'id');
}
public function location()
{
return $this->belongsTo(Location::class);
}
}
Option 1: Lazy Loading:
public function getPlaces()
{
return Place::all()->get();
}
class PlaceResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'createdBy' => (new UserResource($this->createdBy)),
'location' => (new LocationResource($this->location)),
];
}
}
Option 2: Eager Loading:
public function getPlaces()
{
return Place::with([
'createdBy',
'location',
])
->get();
}
class PlaceResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'createdBy' => (new UserResource($this->createdBy)),
'location' => (new LocationResource($this->location)),
];
}
}
Logically thinking with the Eager Loading option data should loads quicker, right? I have tested both options for about 100 places but the loading time seems to be the same.
What's the right way (option to use) to load data quicker?
It is better to Eager Load those relationships when dealing with a result set like that. Since you are iterating through each record in that result set and accessing the relationship this would cause a N+1 issue otherwise. Since it is 2 relationships being accessed it would be (N*2) + 1 .

How to read the worksheet name in laravel Maatwebsite excel

I am having the excel sheet with multiple worksheets in it, I want to get the name of the worksheet which is currently active
I think $event->getSheet()->getTitle() should work. You can also use events also.
class HImport1 implements ToCollection, WithHeadingRow, WithEvents
{
public $sheetNames;
public $sheetData;
public function __construct(){
$this->sheetNames = [];
$this->sheetData = [];
}
public function collection(Collection $collection)
{
$this->sheetData[] = $collection;
}
public function registerEvents(): array
{
return [
BeforeSheet::class => function(BeforeSheet $event) {
$this->sheetNames[] = $event->getSheet()->getTitle();
}
];
}
}
In version 3.1, the API has changed slightly. You'll have to call getDelegate() before getting the title. Building off of #Satendra Rawat answer...
public function registerEvents(): array
{
return [
BeforeSheet::class => function (BeforeSheet $event) {
$this->sheetNames[] = $event->getSheet()->getDelegate()->getTitle();
}
];
}

Laravel Nova + Spatie Media library

Trying to use Laravel Nova with Spatie Media Library. I created upload field like this:
Image::make('Logo')
->store(function (Request $request, $model) {
$model->addMediaFromRequest('logo')->toMediaCollection('manufacturers');
}),
Seams ok, but Nova still trying to save file name to "logo" column in manufacturers table.
Original excample to customize this field was:
File::make('Attachment')
->store(function (Request $request, $model) {
return [
'attachment' => $request->attachment->store('/', 's3'),
'attachment_name' => $request->attachment->getClientOriginalName(),
'attachment_size' => $request->attachment->getSize(),
];
})
I found a work around by setting an empty mutator on the model. In your case it would be:
class Manufacturer extends Model implements HasMedia
{
use HasMediaTrait;
public function setLogoAttribute() {}
//...
}
Here's an example of my entire implementation. Note that currently with Nova 1.0.6, the preview() method is not working, it's returning the thumbnail() url.
App/GalleryItem
class GalleryItem extends Model implements HasMedia
{
use HasMediaTrait;
public function setImageAttribute() {}
public function registerMediaConversions(Media $media = null)
{
$this->addMediaConversion('thumbnail')
->fit(Manipulations::FIT_CROP, 64, 64);
$this->addMediaConversion('preview')
->fit(Manipulations::FIT_CROP, 636, 424);
$this->addMediaConversion('large')
->fit(Manipulations::FIT_CONTAIN, 1920, 1080)
->withResponsiveImages();
}
public function registerMediaCollections()
{
$this->addMediaCollection('images')->singleFile();
}
}
App/Nova/GalleryItem
class GalleryItem extends Resource
{
public static $model = 'App\GalleryItem';
public static $with = ['media'];
public function fields(Request $request)
{
return [
Image::make('Image')
->store(function (Request $request, $model) {
$model->addMediaFromRequest('image')->toMediaCollection('images');
})
->preview(function () {
return $this->getFirstMediaUrl('images', 'preview');
})
->thumbnail(function () {
return $this->getFirstMediaUrl('images', 'thumbnail');
})
->deletable(false);
];
}
}
As with Nova 3 (and Laravel 8) you need to return true from the fillUsing or store method:
File::make('Attachment')
->store(function (Request $request, $model) {
$model->addMediaFromRequest('logo')->toMediaCollection('manufacturers');
return true;
// This will tell nova that you have taken care of it yourself.
})
As soon as you return anything but true nova will assume, that it needs to save something to the database. This leads to an error if the field does not exist in db (as to expect with spatie-medialibrary) or it will overwrite your precious data if the field exists but serves another purpose.
Nova allows you to return true from the callback to indicate that the processing is complete and that it shouldn't set any attributes itself.
This is the code that runs the callback:
protected function fillAttribute(NovaRequest $request, $requestAttribute, $model, $attribute)
{
//...
$result = call_user_func($this->storageCallback, $request, $model);
if ($result === true) {
return;
}
if (! is_array($result)) {
return $model->{$attribute} = $result;
}
foreach ($result as $key => $value) {
$model->{$key} = $value;
}
}
So either true or any empty array will achieve the same thing, but personally feels clearer to do the former.
Image::make('Logo')
->store(function (Request $request, $model) {
$model->addMediaFromRequest('logo')->toMediaCollection('manufacturers');
return [];
}),
Maybe returning an empty array prevent nova from saving the name.

Trying to get property of non-object, but I can if I return it inmediatly

I have a good one here:
Im developing an API and im returning my values with the following code:
public function apigetDrugs(){
$arrayreturn= array();
foreach (Drug::all() as $drug){
$array=[
"pharma"=>$drug->pharma->name,
"id"=>$drug->id,
"code"=>$drug->code,
"CABMS_code"=>$drug->CABMS_code,
"CABMSDF_code"=>$drug->CABMSDF_code,
"name"=>$drug->name,
"concentration"=>$drug->concentration,
"presentation"=>$drug->presentation->name,
"container"=>$drug->container->name,
"previous_stock"=>$drug->previous_stock,
"added"=>$drug->added,
"added_transferred"=>$drug->added_transferred,
"exit"=>$drug->exit,
"exit_transferred"=>$drug->exit_transferred,
"extra"=>$drug->extra,
"user"=>$drug->user->name,
];
$arrayreturn[]=$array;
}
return $arrayreturn;
}
"Drug" model has relationships with Container, Presentation, User and Pharma. When I run my API I get the error "Trying to get property of non-object", so I decided to comment each line of code one by one and I found that I have the code running smoothly if I comment the pharma line, just like this:
public function apigetDrugs(){
$arrayreturn= array();
foreach (Drug::all() as $drug){
$array=[
//"pharma"=>$drug->pharma->name,
"id"=>$drug->id,
"code"=>$drug->code,
"CABMS_code"=>$drug->CABMS_code,
"CABMSDF_code"=>$drug->CABMSDF_code,
"name"=>$drug->name,
"concentration"=>$drug->concentration,
"presentation"=>$drug->presentation->name,
"container"=>$drug->container->name,
"previous_stock"=>$drug->previous_stock,
"added"=>$drug->added,
"added_transferred"=>$drug->added_transferred,
"exit"=>$drug->exit,
"exit_transferred"=>$drug->exit_transferred,
"extra"=>$drug->extra,
"user"=>$drug->user->name,
];
$arrayreturn[]=$array;
}
return $arrayreturn;
}
So I checked my relationships in the Pharma and Drug classes, nothing wrong that I can see with Drug:
class Drug extends Model
{
protected $guarded=[
"id",
"user_id"
];
use SoftDeletes;
protected $dates = ['deleted_at'];
//
public function user(){
return $this->belongsTo("App\User");
}
public function pharma(){
return $this->belongsTo("App\Pharma");
}
public function presentation(){
return $this->belongsTo("App\Presentation");
}
public function unit(){
return $this->belongsTo("App\Unit");
}
public function container(){
return $this->belongsTo("App\Container");
}
}
Nor with Pharma:
class Pharma extends Model
{
//
protected $guarded=[
"id"
];
public function drugs(){
$this->hasMany("App\Drug");
}
public function user(){
$this->belongsTo("App\User");
}
}
However I found something strange. I found this code will run without an issue and display perfectly the string that is intended to show:
public function apigetDrugs(){
$arrayreturn= array();
foreach (Drug::all() as $drug){
dd($drug->pharma->name);
}
}
And it will even work if I replace "dd" function with "echo". Hope to hear what you think about this issue soon.
Luis
Found my problem, one of my seeds had an invalid drug_id value.

How to create self referential relationship in laravel?

I am new to Laravel. I Just want to create a self referential model. For example, I want to create a product category in which the field parent_id as same as product category id. How is this possible?
Model Shown below
class Product_category extends Eloquent
{
protected $guarded = array();
public static $rules = array(
'name' => 'required',
'parent_id' => 'required'
);
function product_category()
{
return $this->belongsto('Product_category','parent_id');
}
}
It results Maximum function nesting level of '100' reached, aborting! Error
You can add a relation to the model and set the custom key for the relation field.
Update:
Try this construction
class Post extends Eloquent {
public function parent()
{
return $this->belongsTo('Post', 'parent_id');
}
public function children()
{
return $this->hasMany('Post', 'parent_id');
}
}
Old answer:
class Post extends Eloquent {
function posts(){
return $this->hasMany('Post', 'parent_id');
}
}
Your model is not at fault for producing the "maximum function nesting level of '100' reached" error. It's XDebug's configuration; increase your xdebug.max_nesting_level.
The following is from a 2015 post by #sitesense on laracasts.com:
This is not a bug in Laravel, Symfony or anything else. It only occurs when XDebug is installed.
It happens simply because 100 or more functions are called recursively. This is not a high figure as such and later versions of XDebug (>= 2.3.0) have raised this limit to 256. See here:
http://bugs.xdebug.org/bug_view_page.php?bug_id=00001100
EDIT: In fact the latest Homestead provisioning script already sets the limit to 250. See line 122 here:
https://github.com/laravel/settler/blob/master/scripts/provision.sh#L122
So the addition of xdebug.max_nesting_level = 250 to php.ini should do it.
I've added a little more to the code based on your comments trying to access the parent!
class Person extends \Eloquent {
protected $fillable = [];
var $mom, $kids;
function __construct() {
if($this->dependency_id<>0) {
$this->mother->with('mother');
}
}
public function children() {
$children = $this->hasMany('Person','dependency_id');
foreach($children as $child) {
$child->mom = $this;
}
return $children;
}
public function mother() {
$mother = $this->belongsTo('Person','dependency_id');
if(isset($mother->kids)) {
$mother->kids->merge($mother);
}
return $mother;
}
}
Then you can access the parent from the child with eager loading, see more here: http://neonos.net/laravel-eloquent-model-parentchild-relationship-with-itself/
you can refer self, using $this
class Post extends Eloquent {
function posts(){
return $this->hasMany($this, 'parent_id');
}
}
Take a look at my answer here.
The key is this code below in Model.php
public function children()
{
return $this->hasMany(Structure::class, 'parent_id')->with('children');
}

Resources