Currently working on an Excel import functionality. I want to import the Excel sheet and display its information on another page using a foreach loop. However it seems to be rather hard for some reason. When I die and dump the collection data (used this because import would error) it shows everything correctly. The data in the spreadsheet is where it should be. So I feel that is fine. However I cannot get it inserted into my database for some odd reason. I have a header row so I use the WithHeaderRow functionality.
DataImport class:
<?php
namespace App\Imports;
use App\Data;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Imports\HeadingRowFormatter;
HeadingRowFormatter::default('none');
class DataImport implements ToModel, WithHeadingRow
{
/**
* #param array $row
*
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function model(array $row)
{
return new Data([
'cliëntnummer' => $row['clientnummer'],
'zoeknaam' => $row['zoeknaam'],
'naam' => $row['naam'],
'omschrijving' => $row['omschrijving'],
'plaats' => $row['plaats'],
'week' => $row['week'],
'vennoot' => $row['vennoot'],
'relatiebeheerder' => $row['relatiebeheerder'],
'samensteller' => $row['samensteller'],
'ADVer' => $row['adver'],
'cliëntgroepcode' => $row['clientgroepcode'],
'accountant' => $row['accountant'],
'samenstellen' => $row['samenstellen'],
'ADV jaarwerk' => $row['ADVJaarwerk'],
'periodieke ADV' => $row['periodiekeADV'],
'fiscaliteiten' => $row['fiscaliteiten'],
]);
}
}
Datacontroller.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Imports\DataImport;
use Maatwebsite\Excel\Facades\Excel;
use App\Http\Controllers\Controller;
class DataController extends Controller
{
public function index(){
return view('importeren');
}
public function import(Request $request){
$datas = Excel::toCollection(new DataImport(), $request->file('import_file'));
dd($datas);
return redirect()->route('/home');
}
}
Data Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Data extends Model
{
protected $fillable = [
'id', 'cliëntnummer', 'zoeknaam', 'naam', 'omschrijving', 'plaats', 'week', 'vennoot', 'relatiebeheerder', 'samensteller', 'ADVer', 'cliëntgroepcode', 'accountant', 'samenstellen', 'ADV jaarwerk', 'periodieke ADV', 'fiscaliteiten',
];
}
Spreadsheet data:
Database table layout:
Datadump results:
If I left anything out feel free to tell me. I hope this is sufficient.
Turns out I forgot to reference my datamodel in the file, after finalizing the code of my controller like below it now works (almost) seamlessly.
<?php
namespace App\Imports;
use App\Data;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Imports\HeadingRowFormatter;
HeadingRowFormatter::default('none');
class DataImport implements ToModel, WithHeadingRow
{
/**
* #param array $row
*
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function model(array $row)
{
return new Data([
'cliëntnummer' => $row['Cliëntnummer'],
'zoeknaam' => $row['Zoeknaam'],
'naam' => $row['Naam'],
'omschrijving' => $row['Omschrijving'],
'plaats' => $row['Plaats'],
'week' => $row['Week'],
'vennoot' => $row['Vennoot'],
'relatiebeheerder' => $row['Relatiebeheerder'],
'samensteller' => $row['Samensteller'],
'ADVer' => $row['ADVer'],
'cliëntgroepcode' => $row['Cliëntgroepcode'],
'accountant' => $row['Accountant'],
'samenstellen' => $row['Samenstellen'],
'ADVJaarwerk' => $row['ADVJaarwerk'],
'PeriodiekeADV' => $row['PeriodiekeADV'],
'fiscaliteiten' => $row['Fiscaliteiten'],
]);
}
}
Related
I want to create a log each time when insertion happens to the files table. That is, everytime when an insertion to the files table happens, an event has to trigger to create a log for it automatically. Log table needs to have 3 columns id, fileid - primary key of files table & logDetails ,where the logDetails column store a msg like 1 file inserted.
i have read this - https://laravel.com/docs/8.x/events and also searched many more , but that couldn't help me to find where should i start. I have created an event page and a listeners page for this. But i don't know what to write in that.The below Controller & model does the insertion to the files table well. And where i need ur help is to code for creating a log for this insertion. Any help is much appreciated.
Controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\FileLogs;
use Illuminate\Support\Facades\Validator;
use App\Events\InsertFileLog;
class FileLogController extends Controller
{
public function insert(Request $request) // insertion
{
$validator = Validator::make(
$request->all(),
[
'orderId' => 'required|integer', //id of orders table
'fileId' => 'required|integer', //id of file_type table
'status' => 'required|string'
]
);
if ($validator->fails()) {
return response()->json($validator->errors(), 400);
}
$obj = new FileLogs();
$obj->orderId=$request->orderId;
$obj->fileId=$request->fileId;
$obj->status=$request->status;
$obj->save();
//dd($obj->id);
if($obj->id!=''){
InsertFileLog::dispatch($order);
return response()->json(['status'=>'success','StatusCode'=> 200, 'message'=>'Successfully Inserted','data'=>$obj]);
}
else{
return response()->json(['status'=>'Failed','message'=>'Insertion Failed'],400);
}
}
Model
class FileLogs extends Model
{
use HasFactory;
use SoftDeletes;
protected $table='files';
protected $fillable = [
'orderId',
'fileId',
'status'
];
}
Event
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use App\Models\FileLogs;
class InsertFileLog
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $order;
public function __construct(FileLogs $order)
{
$this->order = $order;
}
}
Listener
<?php
namespace App\Listeners;
use App\Events\InsertFileLog;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use App\Models\Logs;
class FileLogListener
{
public function __construct()
{
//
}
public function handle(InsertFileLog $event)
{
//
}
}
EventServiceProvider.php
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
];
public function boot()
{
//
}
}
The App\Providers\EventServiceProvider offers a way to register Events and Listeners. The key is the Event, the values are one or multiple Listeners. You should update your code to contain the following:
EventServiceProvider
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
InsertFileLog::class => [
// Listeners in this array will be executed when InsertFileLog was fired.
FileLogListener::class
],
];
public function boot()
{
//
}
}
The FileLogListener will only be triggered after the event InsertFileLog was fired. To do that, you could do the following, by adding: InsertFileLog::dispatch($order); line before return success response in your insert function.
FileLogController
class FileLogController extends Controller
{
public function insert(Request $request) // insertion
{
$validator = Validator::make(
$request->all(),
[
'orderId' => 'required|integer', //id of orders table
'fileId' => 'required|integer', //id of file_type table
'status' => 'required|string'
]
);
if ($validator->fails()) {
return response()->json($validator->errors(), 400);
}
$obj = new FileLogs();
$obj->orderId=$request->orderId;
$obj->fileId=$request->fileId;
$obj->status=$request->status;
$obj->save();
//dd($obj->id);
if($obj->id!=''){
InsertFileLog::dispatch($obj);
// alternatively, the event helper can be used.
// event(new InsertFileLog($obj));
return response()->json(['status'=>'success','StatusCode'=> 200, 'message'=>'Successfully Inserted','data'=>$obj]);
}
else{
return response()->json(['status'=>'Failed','message'=>'Insertion Failed'],400);
}
}
}
FileLogListener
<?php
namespace App\Listeners;
use App\Events\InsertFileLog;
use App\Models\Log;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class FileLogListener
{
public function __construct()
{
//
}
public function handle(InsertFileLog $event)
{
// $event->order contains the order.
// In your question, you have request an example of creating an insertion to the Log table
Log::create([
'fileId' => $event->order->fileId,
'logDetails' => '1 file inserted',
]);
}
}
Model observers
Alternatively, Model observers provide a solution without having to manually register events/listeners, as they listen to Eloquent events of the observed model like: creating, created, updated, deleted, etc.
I have a Model called Browser which has an array of all the browsers.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Browser extends Model
{
const CHROME = 'chrome';
const FIREFOX = 'firefox';
const OPERA = 'opera';
const SAFARI = 'safari';
const MICROSOFTEDGE = 'microsoft edge';
const INTERNETEXPLORER = 'internet explorer';
public static $types = [self::CHROME, self::FIREFOX, self::OPERA, self::SAFARI, self::MICROSOFTEDGE, self::INTERNETEXPLORER];
}
And I have a Seeder that fills the browsers table.
<?php
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class BrowserSeeder extends Seeder
{
public function run()
{
DB::table('browsers')->insert(array(
array('name' => 'chrome'),
array('name' => 'firefox'),
array('name' => 'opera'),
array('name' => 'safari'),
array('name' => 'microsoft edge'),
array('name' => 'internet explorer'),
array('name' => 'other'),
));
}
}
Could I use the $types array from my Browser model to inset into the database? Because to insert you need an associative array, and I would like to keep $types as it is. Could I easily convert it to an associative array in my Seeder without needing an array with the keys but with just 1 key?
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class BrowserSeeder extends Seeder
{
public function run()
{
$arr = array_map( function( $el ) {
return array('name' => $el );
}, App\Browser::$types );
DB::table('browsers')->insert($arr);
}
}
It's of course not necesary to store in $arr but this makes it a little more readable I think.
In Laravel 6 backend rest api app I use ResourceCollection and Resourcem defintion like :
<?php
namespace App\Http\Resources;
use App\Facades\MyFuncsClass;
use Illuminate\Http\Resources\Json\ResourceCollection;
class TaskCollection extends ResourceCollection
{
public function toArray($task)
{
return [
$this->collection->transform(function($task){
return [
'id' => $task->id,
'name' => $task->name,
'slug' => $task->slug,
...
'events' => !empty($task->events) ? $task->events : [],
'events_count' => !empty($task->events_count) ? $task->events_count : 0,
'created_at' => $task->created_at,
'updated_at' => $task->updated_at,
];
}),
];
}
public function with($task)
{
return [
'meta' => [
'version'=>MyFuncsClass::getAppVersion()
]
];
}
}
I found this decision at
Laravel 5.5 API resources for collections (standalone data)
and it works for me, but I dislike the way I got data on client part, so for listing of data defined in control :
return (new TaskCollection($tasks));
I have to write in vue/cli app :
axios.post(this.apiUrl + '/adminarea/tasks-filter', filters, this.credentialsConfig)
.then((response) => {
this.tasks = response.data.data[0]
this.tasks_total_count = response.data.meta.total
this.tasks_per_page = response.data.meta.per_page
and when I need to get 1 item I define in laravel's control :
return (new TaskCollection([$task])); // I have to wrap it as array
and I have to write in vue/cli app :
axios.get(this.apiUrl + '/adminarea/tasks/' + this.task_id, this.credentialsConfig)
.then((response) => {
// console.log('response::')
// console.log(response)
//
this.taskRow = response.data.data[0][0]
I dislike syntax like data.data[0] and data.data[0][0] and even that works for me
In my app/Providers/AppServiceProvider.php I have lines :
<?php
namespace App\Providers;
use Auth;
use Validator;
use Illuminate\Http\Resources\Json\Resource;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Resource::withoutWrapping(); // looks like that does not work for ResourceCollection!
<?php
namespace App\Providers;
use Auth;
use Validator;
use Illuminate\Http\Resources\Json\Resource;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Resource::withoutWrapping();
...
If there is a way to get rid of data.data[0] and data.data[0][0] in vue/cli part ?
MODIFIED 2:
I created new resource with few columns as app/Http/Resources/Skill.php :
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class Skill extends JsonResource
{
public static $wrap = 'skills';
public function toArray($request)
{
return [
'id' => $request->id,
'name' => $request->name,
'user_id' => $request->user_id,
'user_name' => $request->user_name,
'skill_id' => $request->skill_id,
'skill_name' => $request->skill_name,
'rating' => $request->rating,
'created_at' => $request->created_at,
];
}
}
and app/Http/Resources/SkillCollection.php :
<?php
namespace App\Http\Resources;
use App\Facades\MyFuncsClass;
use App\Http\Resources\Skill;
use Illuminate\Http\Resources\Json\ResourceCollection;
class SkillCollection extends ResourceCollection
{
public static $wrap = 'skills';
public function toArray($request)
{
return $this->collection->transform(function($request){
parent::toArray($request);
});
}
and resulting I have empty "skills":[null,null,null,null] results. What is wrong ?
Thanks!
The first thing you can do is to use destructuring assignment to get the response data from your Axios request:
axios.get(this.apiUrl + '/adminarea/tasks/' + this.task_id, this.credentialsConfig)
.then(({ data }) => {
this.taskRow = data.data[0][0]
So now response.data.data[0][0] is shortened to data.data[0][0].
The second thing you can do is, in the toArray function of your TaskCollection class, to return the transformed collection directly instead of wrapping it in an array:
public function toArray($task)
{
return $this->collection->transform(function($task){
return [
'id' => $task->i
...
];
});
}
Now you don't need to use [0] everywhere. So instead of data.data[0][0], you can access a single task with data.data[0].
To avoid having to use [0] for singular responses, you should return a JsonResource instead of a ResourceCollection. For this you would need to move your code from inside $this->collection->transform to a new JsonResource class. For example:
app/Http/Resources/TaskResource.php:
<?php
namespace App\Http\Resources;
use App\Facades\MyFuncsClass;
use Illuminate\Http\Resources\Json\JsonResource;
class TaskResource extends JsonResource
{
public function toArray($task)
{
return [
'id' => $this->id,
'name' => $this->name,
'slug' => $this->slug,
...
];
}
public function with($task)
{
return [
'meta' => [
'version'=>MyFuncsClass::getAppVersion()
]
];
}
}
Then you can return a single resource with
return new TaskResource($task);
In Javascript you will be able to get a single task with data.data now:
axios.get(this.apiUrl + '/adminarea/tasks/' + this.task_id, this.credentialsConfig)
.then(({ data }) => {
this.taskRow = data.data;
You can refactor your TaskCollection class to implicitly use your new TaskResource class for each task:
<?php
namespace App\Http\Resources;
use App\Facades\MyFuncsClass;
use Illuminate\Http\Resources\Json\ResourceCollection;
class TaskCollection extends ResourceCollection
{
public function toArray($request)
{
return parent::toArray($request);
}
public function with($task)
{
return [
'meta' => [
'version'=>MyFuncsClass::getAppVersion()
]
];
}
}
Like I tried to explain in the comments, you won't be able to get away from having to wrap your results in a key (like 'data') while returning non-null values from the with function. If you don't believe me, temporarily remove your with function and you'll see that you'll be able to access your tasks simply with data (if you have disabled wrapping with Resource::withoutWrapping();).
However, one thing you can do is to customise the "wrap" key. For example, for a collection of tasks you might want it to be tasks and for a single task, just task. You can simply add a $wrap property to your TaskCollection and TaskResource classes:
class TaskCollection extends ResourceCollection
{
public static $wrap = 'tasks';
...
class TaskResource extends JsonResource
{
public static $wrap = 'task';
Now you will be able to access a list of tasks from Javascript with data.tasks and a singular task with data.task:
axios.post(this.apiUrl + '/adminarea/tasks-filter', filters, this.credentialsConfig)
.then(({ data }) => {
this.tasks = data.tasks;
axios.get(this.apiUrl + '/adminarea/tasks/' + this.task_id, this.credentialsConfig)
.then(({ data }) => {
this.taskRow = data.task;
I have a User Model as:
<?php
namespace App;
use App\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Notification;
use Illuminate\Notifications\RoutesNotifications;
use Notifications\EmailClientOfAccount;
class User3 extends User
{
use Notifiable;
public $emailList;
protected $fillable = [
'name', 'email',
];
public function __construct($emails)
{
$this->emailList = $emails;
}
public function getEmailList()
{
return $this->emailList;
}
public static function all(array $columns=[])
{
return $emailList;
}
public function routeNotificationForMail($notification)
{
return $this->email;
}
}
Then,and in Controller:
$collection = collect([
[ 'name' => 'user1', 'email' => 'user1#gmail.com', ], [ 'name' => 'user2',
'email' => 'user2#gmail.com', ], [ 'name' => 'user1000', 'email' =>
'user1000#gmail.com', ],
]);
Second Stage:
$u4 = new User3($collection);
when I use :
dd($u4::all());
It show below error:
ErrorException: Declaration of App\User3::all(array $columns = Array)
should be compatible with
Illuminate\Database\Eloquent\Model::all($columns = Array)
I very try for solve it, but dont,
very Thanks for any help to me,
reference from: Laravel error "Declaration of model/model_name should be compatible with Illuminate\Database\Eloquent\Model"
When overriding a method from parent class - the signature of the method must be exactly the same in terms of parameters and their types
In the parent class, both $attributes and $options are set to be of type array, so you must also set set them this way in your class
namespace App\Models;
class User extends \Illuminate\Database\Eloquent\Model {
...
public function update(array $attributes = [], array $options = []) {
// ... your implementation
return parent::update($attributes, $options);
}
...
}
It show below error: ErrorException: Declaration of App\User3::all (array $columns = Array) should be compatible with Illuminate\Database\Eloquent\Model::all ($columns = Array)
Check that potions. they are not same. May be this is the reason. focus on this may helps you.
When I try to save data from laravel form to a database table I am getting the following exception:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'db.store' doesn't exist (SQL: select count(*) as aggregate from store where name = samplename)
the table store exists but still I am getting the error
this is my contoller that is processing the form:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\storestore;
use App\Http\Requests\storeFormRequest;
class AddstoreController extends Controller
{
//
public function create()
{
//
}
public function store( storeFormRequest $request)
{
$store = new Store;
$store->name = Input::get('name');
$store->description = Input::get('description');
$store->store_vendor_id = Input::get('owner');
$store->contact_email = Input::get('contact_email');
$store->postal_address = Input::get('postal_address');
$store->city = Input::get('city');
$store->zip = Input::get('zip');
$store->phone = Input::get('phone');
$store->business_logo = Input::get('logo');
$store->save();
return \Redirect::route('add_store_success')
->with('message', 'Thanks for joining us!');
}
}
This is my Store model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Store extends Model
{
//
protected $table = 'stores';
protected $fillable = ['name', 'description', 'vendor_id',
'contact_email','postal_address','city','zip','phone',
'meta_description','business_logo'];
}
StoreRequest file:
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
use App\StoreController;
class StoreFormRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
//
'name' => 'required|unique:dstore',
'vendor_id' => 'required',
'contact_email' => 'required|email|max:100|unique:dstore',
'business_logo' => 'required',
];
//validate
if ($validation->fails())
{
return redirect()->back()->withErrors($v->errors());
}
}
}
These are the get and post routes:
Route::get('/store_form', ['as' => 'add_store_form', 'uses' => 'StoreController#create']);
Route::post('/store_form',['as' => 'dstore', 'uses' => 'StoreController#store']);
Both routes are listed when I run php artisan route:list command
I have tried to goggle for solution but the one I landed on pointed out to missing tables as a course, but in my case the store table is existing but still I am getting the error.
Any help please!
Look at your Store model class:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Store extends Model
{
//
protected $table = 'stores';
protected $fillable = ['name', 'description', 'vendor_id',
'contact_email','postal_address','city','zip','phone',
'meta_description','business_logo'];
}
As you see property $table is set to stores so I assume table name in your database is stores and not store.
You should probably change in your StoreFormRequest content or rules method to use in unique rule valid table name, for example:
public function rules()
{
return [
//
'name' => 'required|unique:stores',
'vendor_id' => 'required',
'contact_email' => 'required|email|max:100|unique:stores',
'business_logo' => 'required',
];
//validate
if ($validation->fails())
{
return redirect()->back()->withErrors($v->errors());
}
}