Laravel Export: Attempt to read property "second_key" on null - php

I need to export data with Laravel Export, but the code return ErrorException: Attempt to read property "second_key" on null.
My code:
<?php
namespace App\Admin\Extensions;
use Encore\Admin\Grid\Exporters\ExcelExporter;
use Maatwebsite\Excel\Facades\Excel;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
class DataExporter extends ExcelExporter implements FromQuery, WithMapping, ShouldAutoSize
{
protected $fileName = 'Export Data.xlsx';
public function headings(): array
{
return [
'ID',
'Title',
'Status',
'Gender',
'Data',
];
}
public function map($data): array
{
return [
$data->id,
$data->title,
$data->status,
$data->gender,
$data->json_data->second_key, // <-- Here's the error
];
}
}
I've tried to check using this:
print_r(json_encode($data->json_data));
and this is the result:
{
"id": 282,
"second_key": "second_value",
"third_key": "6200",
"fourth_key": "0000",
"fifth_key": 28
}
I've also done this:
return [
$data->id,
$data->title,
$data->status,
$data->gender,
$data->json_data //Without "second_key"
];
and the excel cell returns the same result:
{
"id": 282,
"second_key": "second_value",
"third_key": "6200",
"fourth_key": "0000",
"fifth_key": 28
}

As #dbf said in the comment section, I have to handle empty rows. I have checked several times in the database, and maybe I missed that one blank row.
Anyway, this is how I handle those values:
if (!isset($data->json_data->second_key)) {
$second_key = '-';
} else {
$second_key = $data->json_data->second_key;
}
return [
$data->id,
$data->title,
$data->status,
$data->gender,
$second_key
];

Related

How to import data from Excel to mysql Database in Laravel? (php, laravel, mysql)

[Update: csv file is succeed in importing data to mysql database, but xls/xlsx still got 'Undefined offset: 1' error]
When I input the Excel file and submit it to import, what I got is error 'Undefined offset: 1'
When I do dd($row) what I got is somehow working?
array:3 [▼ 0 => 1 1 => "XI TKJ" 2 => "Kelas XI TKJ" ]
Screenshot of dd($row) result
Error:
Data in excel I want to input:
Apparently, there's already data on the table but I don't know if it would affect the result or not:
File that got the error -> jurusanImport.php(updated)
use App\jurusan;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
use Maatwebsite\Excel\Concerns\WithConditionalSheets;
class jurusanImport implements ToModel
{
/**
* #param array $row
*
* #return \Illuminate\Database\Eloquent\Model|null
*/
use WithConditionalSheets;
public function conditionalSheets(): array
{
return [
'Worksheet 1' => new FirstSheetImport(),
'Worksheet 2' => new SecondSheetImport(),
'Worksheet 3' => new ThirdSheetImport(),
];
}
public function model(array $row)
{
dd($row);
return new jurusan([
'nama_jurusan' => $row[1],
'deskripsi' => $row[2],
]);
}
}
web.php
Route::post('/Admin/Jurusan/import_excel', 'AdminController#import_Jurusan')
->middleware('role:Admin');
Route::get('/admin/Jurusan/List', 'AdminController#showJurusanList')
->middleware('role:Admin');
AdminController.php
public function import_Jurusan(Request $request)
{
$user = Auth::user();
$this->validate($request, [
'file' => 'required|mimes:csv,xls,xlsx'
]);
$file = $request->file('file');
$nama_file = rand().$file->getClientOriginalName();
$file->move('file_teacher',$nama_file);
$import = new jurusanImport;
$import->onlySheets('Worksheet 1');
Excel::import($import, public_path('/file_teacher/'.$nama_file));
Session::flash('sukses','Data Siswa Berhasil Diimport!');
return redirect()->back();
}
public function showJurusanList(Request $request)
{
$user = Auth::user(); // Untuk Photo Profile
$jurusan = jurusan::get(); // Show, atau Get All "Materi"
return view('pages.admin.kelas.showjurusan', compact('jurusan', 'user') );
}
Model jurusan.php
protected $fillable = [
'nama_jurusan', 'deskripsi',
];
protected $table = 'jurusan';
How can I solve this?
You have multiple sheet in your xls file. You can check https://docs.laravel-excel.com/3.1/imports/multiple-sheets.html#selecting-sheets-by-worksheet-index
This is not your actual code. I am just sharing to you for reference.
namespace App\Imports;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
use Maatwebsite\Excel\Concerns\WithConditionalSheets;
class UsersImport implements WithMultipleSheets
{
use WithConditionalSheets;
public function conditionalSheets(): array
{
return [
'Worksheet 1' => new FirstSheetImport(),
'Worksheet 2' => new SecondSheetImport(),
'Worksheet 3' => new ThirdSheetImport(),
];
}
}
and in controller
$import = new UsersImport();
$import->onlySheets('Worksheet 1', 'Worksheet 3');
Excel::import($import, 'users.xlsx');

Intervention Image with Laravel Resource API

Is it possible to return a image created on the fly with Laravel API Resources without saving it?
Tried in controller
$sum = Product::where('slug', $product)->first();
$img = Image::make('image_path')->resize(400, 400);
$img->text(name, 205, 138, function($font) {
$font->file('fonts/BostonHeavy.woff');
$font->size(42);
$font->color('#ffffff');
$font->align('center');
$font->valign('top');
});
return (new ProductResource($sum))->foo($img);
Laravel API Resource
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class ProductResource extends JsonResource
{
protected $image;
public function foo($value){
$this->image = $value;
return $this;
}
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'image' => $this->image,
];
}
}
what gets returned
{
"data": {
"id": 1,
"name": "Such a pickle",
"image": {
"encoded": "",
"mime": "image/jpeg",
"dirname": null,
"basename": null,
"extension": null,
"filename": null
}
}
}
This obviously is not working in the docs it says to use return $img->response('jpg'); which on it's own works but I want to added it to the response instead of doing two get requests.
From what I understand you want to return something you can use inside a src attribute in an img tag. Try this:
$imgBase64 = (string) $img->encode('data-url');
return (new ProductResource($sum))->foo($imgBase64);
Then, use that string in an img tag with the appropriate syntax:
<img src="data:image/jpeg;base64, <<yourEncodedStringHere>>" />

Map query result with multiple headings using laravel excel

I am exporting an excel using Laravel excel 3.1 by Maatwebsite. I want to map it with 2 headings and display it one below the other
I'm using WithHeadings, WithMapping, FromArray
I'm getting the records, just the format that needs correction
<?php
namespace App\Exports;
use Modules\Program\Entities\Program;
use Modules\report\Entities\report;
use DB;
use Maatwebsite\Excel\Concerns\FromArray;
use Maatwebsite\Excel\Concerns\WithTitle;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithMapping;
class reportPerDaySheet implements FromArray, WithTitle, WithHeadings, ShouldAutoSize, WithMapping
{
private $year;
private $day;
private $month;
public function __construct(int $year, int $month, int $day)
{
$this->year = $year;
$this->day = $day;
$this->month = $month;
}
public function array():array
{
$reportOut = DB::table('reports')->join('programs','programs.program_id','=','reports.program_id')->whereMonth('report_dateofreport', $this->month)->whereday('report_dateofreport', $this->day)->get()->toArray();
return $reportOut;
}
public function map($reportOut):array
{
return [
[
$reportOut->program_programname,
$reportOut->program_projectlead,
$reportOut->program_dse,
$reportOut->program_extserviceprovider,
$reportOut->program_programid,
$reportOut->program_datatype,
],
[
$reportOut->report_id,
$reportOut->report_date,
$reportOut->report_url,
$reportOut->report_username,
$reportOut->report_reportertype,
$reportOut->report_productname,
$reportOut->report_verbatim,
$reportOut->report_priority,
$reportOut->report_aeraised,
]
];
}
public function title(): string
{
return $this->day .' '. date("F", mktime(0,0,0,$this->month,1)) .' '. $this->year;
}
public function headings(): array
{
return[
[
'Program Name',
'Project Lead',
'DS&E Contact',
'Name of external service provider',
'Prepared By',
'External Service Provider Contact Information',
'Time Period Covered',
'Program ID',
'Date of Report',
'Data Type',
'Signature'
],
[
'Id',
'Date of report',
'Date',
'URL',
'Username',
'Reporter Type',
'Product Name',
'Verbatim',
'Priority',
'AE Raised',
'User',
'Date From',
'Date Till',
'Record Created At',
'Record Updated At'
]
];
}
}
Current Output:
Desired Output:
In your situation it would be recommended to build the array including the headers within the array() method. Heading rows will always be displayed as the first x rows. (Depending on how many arrays you return)
I ran into a similar situation where i needed to add headings at certain rows,
Think of the function map() as a foreach() it loops through all your data which means you can add a few checks in and add your headings as an additional row.
have a look at the laravel excel docs
Example of map function:
public function map($reportOut): array
{
if($this->addHeadingRow == 1) {
$this->addHeadingRow = 0; // for my use case I only want to add the heading once
$map = [
[], // add an empty array if you would like an spacer row
[
'heading 1',
'heading 2',
'and so on...'
]
];
}
else{
$map = [
$reportOut->program_programname,
$reportOut->program_projectlead,
$reportOut->program_dse,
$reportOut->program_extserviceprovider,
$reportOut->program_programid,
$reportOut->program_datatype,
];
}
return $map;
}

Laravel how use value instead of full object with join in eloquent model

When I call eloquent:
$user = User::where('idUser', 1)->with(['privilege'])->first()->toArray();
It gives me:
{
"idUser": 1,
"name": "UserName",
"email": "UserName#gmail.com",
"image": "https://image.com",
"createdAt": "2019-05-07 15:43:47",
"privilege": {
"idPrivilege": 1,
"name": "user"
}
}
When I call Eloquent:
$user = User::where('idUser', 1)->with(['privilege:name'])->first()->toArray();
Element privilege in json is set to null, but when I call:
$user = User::where('idUser', 1)->with(['privilege:idPrivilege,name'])->first()->toArray();
It is as same as first call. How can I set element privilege to f.e. user (I just want a simple value instead of the full object of Privilege)?
I can use something like:
$user['privilege'] = $user['privilege']['name'];
But this one does not look so nice!
Using resource:
public function toArray($request)
{
return [
'idUser' => $this->idUser,
'name' => $this->name,
'email' => $this->email,
'privilege' => $this->privilege['name'],
'createdAt' => $this->created_at,
];
}
In controller:
$user = User::where('idUser', 1)->with('privilege')->first();
return UserResource::make($user);
Gives:
{
"data": {
"idUser": 1,
"name": "UserName",
"email": "UserName#gmail.com",
"privilege": "user",
"createdAt": "2019-05-07 15:43:47"
}
}
How can i just return object instead of data{object} ?
Try without the backets:
$user = User::where('idUser', $id)->with('privilege:name')->first()->toArray();
or this:
$user = User
::where('idUser', $id)
->with(['privilege' => function($query) {
return $query->select('name');
}])
->first()
->toArray();
But then, you could customize the response to return to your view using API Resources. With this, you can have many different resources to use on the same elements and format the response to any of your needs.
As stated in HCK's answer, you could use
$user = User
::where('idUser', $id)
->with(['privilege' => function($query) {
return $query->select('name');
}])
->first()
->toArray();
To get what you need. Now, if you are already using API Resources, and want to remove the outter data object, you can add the following in your AppServiceProvider boot method:
use Illuminate\Http\Resources\Json\Resource;
class AppServiceProvider extends ServiceProvider
{
/**
* Perform post-registration booting of services.
*
* #return void
*/
public function boot()
{
Resource::withoutWrapping(); // With this, your resources won't have the
// outter data wrapping
}
}
Just take a look at the docs!

Modify data before pagination in CakePhp

I'm trying to create an Api using cakephp.
I generate a json on server and it works fine , but I tired to use pagination and I got a problem.
in the first case I take the image's path and I encode it to base64 and I generate json => works
in the second case I defined the pagination by the limits and the max and I kept the same code but as a result the image field is still the path from the database and it's not encoded
this my code in my controller :
class PilotsController extends AppController {
public $paginate = [
'page' => 1,
'limit' => 5,
'maxLimit' => 5
];
public function initialize() {
parent::initialize();
$this->loadComponent('Paginator');
$this->Auth->allow(['add','edit','delete','view','count']);
}
public function view($id) {
$pilot = $this->Pilots->find()->where(['Pilots.account_id' => $id], [
'contain' => ['Accounts', 'Pilotlogs']
]);
foreach ($pilot as $obj) {
if ($obj->image_pilot!= NULL) {
$image1 = file_get_contents(WWW_ROOT.$obj->image_pilot);
$obj->image_pilot = base64_encode($image1);
}
}
$this->set('pilot', $this->paginate($pilot));
$this->set('_serialize', ['pilot']);
}
}
If I remove the pagination from the code it works fine . Any idea how to fix it ??
I'd suggest to use a result formatter instead, ie Query::formatResults().
So you'll have something like this :
public function view($id) {
$pilot = $this->Pilots->find()
->where(['Pilots.account_id' => $id], [
'contain' => ['Accounts', 'Pilotlogs']]);
->formatResults(function($results) {
return $results->map(function($row) {
$image1 = file_get_contents(WWW_ROOT.$row['image_pilot']);
$row['image_pilot'] = base64_encode($image1);
return $row;
});
});
}
You can simply first paginate the data and then get the array values and after that modify that data as you want. Check this
public function view($id) {
$pilot = $this->Pilots->find()->where(['Pilots.account_id' => $id], [
'contain' => ['Accounts', 'Pilotlogs']
]);
$pilot = $this->paginate($pilot);
$pilot = $pilot->toArray();
foreach ($pilot as $obj) {
if ($obj->image_pilot!= NULL) {
$image1 = file_get_contents(WWW_ROOT.$obj->image_pilot);
$obj->image_pilot = base64_encode($image1);
}
}
$this->set('pilot', $pilot);
$this->set('_serialize', ['pilot']);
}

Categories