Styling laravel excel using from view - excel

This is the controller
public function dxport()
{
//set excel style
$styleArray = [
'font' => [
'bold' => true,
],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
],
];
//download excel with style
return Excel::download(new FieldequipsExport, 'users.xlsx')->setStyle($styleArray);
}
This is the export
<?php
namespace App\Exports;
use App\Models\Fieldequip;
use App\Models\Vendor;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
use Maatwebsite\Excel\Concerns\WithStyles;
use Maatwebsite\Excel\Facades\Excel;
class testExport implements FromView
{
//style the excel
public function __construct()
{
}
public function view(): View
{
$matchThese = ['type' => '1'];
$vendor = Vendor::pluck('vendor_name');
$data = Fieldequip::where($matchThese)->get();
$data->setFontSize(15);
return view('livewire.expfield',['data' => $data],['vendor' => $vendor]);
}
}
It returns an error which is [Call to undefined method Symfony\Component\HttpFoundation\BinaryFileResponse::setStyle()]
I read the documentation but still ran into this problem. How do I style it correctly?

Related

$this->request->isAJAX() undefined method in codeigniter 4.1.1 where is the problem?

I'm having a problem with my controller, when I try to call request->isAJAX() the program returns the error undefined method
Note : i'm using codeigniter 4.1.1
<?php
namespace App\Controllers;
use App\Models\Mruang;
class Ruang extends BaseController
{
protected $jenis;
public function __construct()
{
$this->jenis = new Mruang();
}
public function index()
{
$data = [
'titel' => 'Jenis Ruang'
];
return view('ruang/index', $data);
}
public function tampil()
{
if ($this->request->isAJAX()) {
$data = [
'ruang' => $this->jenis->findAll(),
'btn' => true
];
$msg = [
'data' => view('ruang/data', $data)
];
echo json_encode($msg);
}
}
}
I run your code at my local machine in a CI 4.1.1 installation and it runs ok, at least the $this->request->isAJAX() worked as expected.
Look at your BaseController and compare it with the default. I pasting the default code bellow
<?php
namespace App\Controllers;
use CodeIgniter\Controller;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;
class BaseController extends Controller
{
protected $helpers = [];
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
{
// Do Not Edit This Line
parent::initController($request, $response, $logger);
}
}
I also would like to recommend you to look if Mruang model has the findAll() methods.

Codeigniter redirect()->to() doesnt work in __construct()

I have code in my controller
public function __construct()
{
return redirect()->to('/auth');
$this->validation =
\Config\Services::validation();
$this->title = 'Header Menu';
$this->controller = 'user';
}
public function index()
{
$data = [
'title_prefix' => 'Profil',
'title' => $this->title,
'controller' => $this->controller,
'button_add' => false,
'validation' => $this->validation
];
return view('user/index', $data);
}
it still show view('user/index'). How to get to return redirect()->to('/auth') in __construct() ?
sorry i'm not speaking english well
It is an expected behavior that redirect() doesn't work inside a constructor.
redirect() in CI4 doesn't just set headers but return a RedirectResponse object.
Problem is : while being in the constructor of your controller, you can't return an instance of something else. You're trying to construct a Controller not a RedirectResponse.
Good practice is to use Controller Filters
Or you could add the redirect() inside your index function if there's only at this endpoint that you would like to redirect the user.
Here's an example of the filter that would fit your need :
Watch out your CI version. The parameter $arguments is needed since 4.0.4. Otherwise you have to remove it
<?php
namespace App\Filters;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Filters\FilterInterface;
class AuthFilter implements FilterInterface {
public function before(RequestInterface $request, $arguments = null) {
return redirect()->to('/auth');
}
//--------------------------------------------------------------------
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) {
}
}
And in your app/Config/Filters edit those 2 variables in order to activate your filter :
public $aliases = [
'auth' => \CodeIgniter\Filters\AuthFilter::class,
];
public $globals = [
'before' => ['auth' => 'the_routes_you_want_to_redirect']
];
You might want to check this thread aswell : https://forum.codeigniter.com/thread-74537.html
you can't use redirect() inside __construct() function or initController() directly.
But you can use $response parameter in initController() or $this->response attribute.
https://stackoverflow.com/a/65814413/1462903
in controller class
<?php namespace App\Controllers;
class Data extends BaseController
{
public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger)
{
// Do Not Edit This Line
parent::initController($request, $response, $logger);
if($this->session->get('is_loggedin') !== true){
$response->redirect(base_url('login')); // or use $this->response->redirect(base_url('login'));
}
}
public function index()
{
return view('dashboard');
}
}
in BaseController class
/**
* Constructor.
*/
public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger)
{
// Do Not Edit This Line
parent::initController($request, $response, $logger);
//--------------------------------------------------------------------
// Preload any models, libraries, etc, here.
//--------------------------------------------------------------------
$this->session = \Config\Services::session();
}
header("Location: ".site_url().'/dashboard');
die();

How can apply style using withEvents in multiplesheets in maatwebsite 3.1?

I have used laravel excel version 2.1 previously . In version 2.0 cell designing was pretty easy . So i started a new project in latest edition of laravel (6) and was shocked to know that we can't use laravel excel version 2.0 in this version of laravel . I installed latest version of laravel-excel 3.1 and sheet styling is totally different from the version larave-excel version 2.0 so i dig into documentation i found this custom styles using registerEvents for the multiple sheets but it didn't work.
Here is my Export class.
<?php
namespace App\Exports;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
use Maatwebsite\Excel\Concerns\WithEvents;
use Excel;
use App\Exports\ExportSheet;
use Maatwebsite\Excel\Events\AfterSheet;
use Maatwebsite\Excel\Sheet;
class ExcelExport implements WithMultipleSheets, WithEvents
{
use Exportable;
/**
* #var Invoice $invoice
*/
protected $excel_input;
private $writerType = 'xlsx';
public function __construct($excel_input)
{
$this->excel_input = $excel_input;
}
public function sheets(): array
{
$data = $this->excel_input;
$sheets = [];
foreach($data as $sheet_data) {
$sheets[] = new ExportSheet($sheet_data);
}
return $sheets;
}
public function registerEvents(): array
{
$normal_style = array('font' => array('name' => 'Times New Roman','size' => 15));
return [
AfterSheet::class => function(AfterSheet $event) use($normal_style) {
$event->sheet->getStyle("A:Z")->getAlignment()->setWrapText(true);
$event->sheet->getStyle("A:Z")->applyFromArray($normal_style);
$event->sheet->setAutoSize(true);
$event->sheet->setScale('100');
$event->sheet->setFitToHeight(false);
$event->sheet->setFitToWidth(true);
$event->sheet->setPageMargin(0.25);
},
];
}
}
Here is my ExportSheet file for generate multiple sheets.
<?php
namespace App\Exports;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithTitle;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\FromView;
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
use Illuminate\Contracts\View\View;
use PhpOffice\PhpSpreadsheet\Style\Style;
class ExportSheet implements WithTitle, ShouldAutoSize, FromView, WithColumnFormatting
{
private $sheet_data;
public function __construct(array $sheet_data)
{
$this->sheet_data = $sheet_data;
}
/**
* #return string
*/
public function title(): string
{
$data = $this->sheet_data;
return $data['sheet_name'];
}
public function ShouldAutoSize()
{
return true;
}
public function view(): View
{
$info = $this->sheet_data;
return view($info['template_name'])->with(['data'=>$info]);
}
}```
I had same problem. the registerEvents worked fine without implements WithMultipleSheets. But after implements WithMultipleSheets, it didn't work anymore.
Is there anyone give me some advices please?

Symfony3 forms not showing input fields when using CollectionType

I'm having problem with displaying form when using CollectionType. It doesn't show newOrderCustomerType inputs, just label "Customer Id". Whats wrong?
newOrderCustomerType
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class newOrderCustomerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->
add('firstname', TextType::class,array('label'=>'Firstname'))->
add('lastname', TextType::class,array('label'=>'Lastname'))->
add('email', TextType::class,array('label'=>'Email'))->
add('login', TextType::class,array('label'=>'Login'));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Customer',
));
}
public function getName()
{
return 'app_bundlenew_order_customer_type';
}
}
newOrderType
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class newOrderType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('customerId',CollectionType::class,array(
'entry_type'=>newOrderCustomerType::class,
'allow_add' => true,
'by_reference' => false,
'data_class' => 'AppBundle\Entity\Customer',
))
->add('shopOrderId')
->add('orderDate')
->add('postBuyFormMsg')
->add('invoice')
->add('payType')
->add('shipmentType')
->add('payStatus')
->add('save',SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
}
public function getName()
{
return 'app_bundlenew_order_type';
}
}
And in TWIG template
{{ form_start(orderForm) }}
{{ form_widget(orderForm) }}
{{ form_end(orderForm) }}
How to make it show all input fields?
If you have an active "allow_add" option, it is possible to render this input throught the 'prototype' option:
$builder
->add('customerId',CollectionType::class,array(
'entry_type'=>newOrderCustomerType::class,
'allow_add' => true,
'by_reference' => false,
'data_class' => 'AppBundle\Entity\Customer',
'prototype' => true,
))
and then in the form:
{{ form_row(orderForm.customerId.vars.prototype}) }}
It should work.
For more, see the Symfony documentation of prototype option
Try removing 'data_class', I don't think that's part of CollectionType:
->add('customerId',CollectionType::class,array(
'entry_type'=>newOrderCustomerType::class,
'allow_add' => true,
'by_reference' => false,
))
See if that works.

Form: Avoid setting null to non submitted field

I've got a simple model (simplified of source):
class Collection
{
public $page;
public $limit;
}
And a form type:
class CollectionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('page', 'integer');
$builder->add('limit', 'integer');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'FSC\Common\Rest\Form\Model\Collection',
));
}
}
My controller:
public function getUsersAction(Request $request)
{
$collection = new Collection();
$collection->page = 1;
$collection->limit = 10;
$form = $this->createForm(new CollectionType(), $collection)
$form->bind($request);
print_r($collection);exit;
}
When i POST /users/?form[page]=2&form[limit]=20, the response is what i expect:
Collection Object
(
[page:public] => 2
[limit:public] => 20
)
Now, when i POST /users/?form[page]=3, the response is:
Collection Object
(
[page:public] => 3
[limit:public] =>
)
limit becomes null, because it was not submitted.
I wanted to get
Collection Object
(
[page:public] => 3
[limit:public] => 10 // The default value, set before the bind
)
Question: How can i change the form behaviour, so that it ignores non submitted values ?
If is only a problem of parameters (GET parameters) you can define the default value into routing file
route_name:
pattern: /users/?form[page]={page}&form[limit]={limit}
defaults: { _controller: CompanyNameBundleName:ControllerName:ActionName,
limit:10 }
An alternative way could be to use a hook (i.e. PRE_BIND) and update manually that value into this event. In that way you haven't the "logic" spreaded into multi pieces of code.
Final code - suggested by Adrien - will be
<?php
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvents;
class IgnoreNonSubmittedFieldSubscriber implements EventSubscriberInterface
{
private $factory;
public function __construct(FormFactoryInterface $factory)
{
$this->factory = $factory;
}
public static function getSubscribedEvents()
{
return array(FormEvents::PRE_BIND => 'preBind');
}
public function preBind(FormEvent $event)
{
$submittedData = $event->getData();
$form = $event->getForm();
// We remove every child that has no data to bind, to avoid "overriding" the form default data
foreach ($form->all() as $name => $child) {
if (!isset($submittedData[$name])) {
$form->remove($name);
}
}
}
}
Here's a modification of the original answer. The most important benefit of this solution is that validators can now behave as if the form post would always be complete, which means there's no problems with error bubbling and such.
Note that object field names must be identical to form field names for this code to work.
<?php
namespace Acme\DemoBundle\Form;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvents;
class FillNonSubmittedFieldsWithDefaultsSubscriber implements EventSubscriberInterface
{
private $factory;
public function __construct(FormFactoryInterface $factory)
{
$this->factory = $factory;
}
public static function getSubscribedEvents()
{
return array(FormEvents::PRE_BIND => 'preBind');
}
public function preBind(FormEvent $event)
{
$submittedData = $event->getData();
$form = $event->getForm();
// We complete partial submitted data by inserting default values from object
foreach ($form->all() as $name => $child) {
if (!isset($submittedData[$name])) {
$obj = $form->getData();
$getter = "get".ucfirst($name);
$submittedData[$name] = $obj->$getter();
}
}
$event->setData($submittedData);
}
}

Resources