How to make distinct inserts using a custom array using database seeder?
Using the following code:
$categories = ['Hardware', 'Software', 'Planning', 'Tools'];
foreach ($categories as $category) {
App\Category::insert([
'name' => $category,
'slug' => Str::slug($category),
]);
}
It doesn't work without a factory for a category that is the problem matter if I use insert or create.
It gives this error
Unable to locate factory for [App\Category].
at vendor/laravel/framework/src/Illuminate/Database/Eloquent/FactoryBuilder.php:273
269| */
270| protected function getRawAttributes(array $attributes = [])
271| {
272| if (! isset($this->definitions[$this->class])) {
> 273| throw new InvalidArgumentException("Unable to locate factory for [{$this->class}].");
274| }
275|
276| $definition = call_user_func(
277| $this->definitions[$this->class],
Thanks
Create an array of data in the run method of you seeder file
public function run()
{
$categories = [
['name' => 'Music'],
['name' => 'Gaming'],
['name' => 'Entertainment'],
['name' => 'Non-Profit & Activism'],
['name' => 'Other'],
];
foreach ($categories as $category) {
Category::create($category);
}
}
You are pushing all data into the Model, so you need to set fillable or guarded in the Model.
class Category extends Model
{
protected $guarded = [];
}
try using insert witch take an array as parameter:
public function run()
{
$inputs[] = ['name'=> 'Hardware'];
$inputs[] = ['name'=> 'Software'];
$inputs[] = ['name'=> 'Planning'];
$inputs[] = ['name'=> 'Tools'];
App\Category::insert($inputs);
}
or you can do it in another way:
public function run()
{
$inputsNames = ['Hardware', 'Software', 'Planning', 'Tools'];
foreach($inputsNames as $categoryName)
{
$inputs[]=['name'=>$categoryName];
}
App\Category::insert($inputs);
}
Related
In my Laravel-8 project, I have this controller for Input Field Array Update.
Controller:
public function update(UpdateSaleRequest $request, $id)
{
try {
$sale = Sale::find($id);
$data = $request->all();
$update['date'] = date('Y-m-d', strtotime($data['date']));
$update['company_id'] = $data['company_id'];
$update['name'] = $data['name'];
$update['remarks'] = $data['remarks'];
$sale->update($update);
SaleDetail::where('sale_id', $sale->id)->delete();
foreach ($data['invoiceItems'] as $item) {
$details = [
'sale_id' => $sale->id,
'item_id' => $item['item_id'],
'employee_id' => $item['employee_id'],
'quantity' => $item['qty'],
'price' => $item['cost'],
'total_price' => $item['cost'] * $item['qty'],
'sale_type_id'=>$item['sale_type_id']
];
$saleDetail = new SaleDetail($details );
$saleDetail->save();
}
} catch (JWTException $e) {
throw new HttpException(500);
}
return response()->json($sale);
}
In the form, the user can add more Sales Detail or remove.
Some of the SaleDetail fields are being used somewhere else.
Is there a way to update the input field array without deleting the SaleDetail as shown in what I did here:
SaleDetail::where('sale_id', $sale->id)->delete();
Thanks
I've tried to restructure your code so that's easier to edit. I've left some comments. I can really recommend refactoring.guru. There you will find many ways to improve your code so that it is more extensible, maintainable and testable. If you have any questions, please feel free to ask.
class Sale extends Model
{
// Use a relationship instead of building your own query
public function details() {
return $this->hasMany(SaleDetail::class);
}
}
class SaleDetail extends Model
{
// Use a computed property instead of manually calculating total price
// You can access it with $saleDetail->totalPrice
public function getTotalPriceAttribute() {
return $this->price * $this->quantity;
}
}
class UpdateSaleRequest extends Request
{
public function authorize() {
return true;
}
protected function prepareForValidation() {
$this->merge([
// Create a Carbon instance by string
'date' => Carbon::make($this->date)
]);
}
public function rules() {
// Your validation rules
// Please also validate your invoice items!
// See https://laravel.com/docs/8.x/validation#validating-arrays
}
}
// We let Laravel solve the sale by dependency injection
// You have to rename the variable name in ihr web.php
public function update(UpdateSaleRequest $request, Sale $sale)
{
// At this point, all inputs are validated!
// See https://laravel.com/docs/8.x/validation#creating-form-requests
$sale->update($request->validated());
// Please ensure, that all properties have the same name
// In your current implementation you have price = cost, be consistent!
foreach($request->input('invoiceItems') as $invoiceItem) {
// How we can consider that a detail is already created?
// I assume that each item_id will only occur once, otherwise you'll
// place the id of each detail in your update form (e.g. in a hidden input)
$candidate = $sale->details()
->where('item_id', $properties['item_id'])
->first();
if($candidate) {
$candidate->update($properties);
} else {
$sale->details()->create($properties);
}
}
// A JWT-Exception should not be necessary, since your authentication
// will be handled by a middleware.
return response()->json($sale);
}
I have not tested the code, few adjustments may be needed.
Laravel has a method called updateOrCreate as follow
/**
* Create or update a record matching the attributes, and fill it with values.
*
* #param array $attributes
* #param array $values
* #return \Illuminate\Database\Eloquent\Model|static
*/
public function updateOrCreate(array $attributes, array $values = [])
{
return tap($this->firstOrNew($attributes), function ($instance) use ($values) {
$instance->fill($values)->save();
});
}
That means you could do some thing like
public function update(UpdateSaleRequest $request, $id)
{
try {
$sale = Sale::find($id);
$data = $request->all();
$update['date'] = date('Y-m-d', strtotime($data['date']));
$update['company_id'] = $data['company_id'];
$update['name'] = $data['name'];
$update['remarks'] = $data['remarks'];
$sale->update($update);
foreach ($data['invoiceItems'] as $item) {
$details = [
'item_id' => $item['item_id'],
'employee_id' => $item['employee_id'],
'quantity' => $item['qty'],
'price' => $item['cost'],
'total_price' => $item['cost'] * $item['qty'],
'sale_type_id'=>$item['sale_type_id']
];
$sale->saleDetail()->updateOrCreate([
'sale_id' => $sale->id
], $details);
}
} catch (JWTException $e) {
throw new HttpException(500);
}
return response()->json($sale);
}
I would encourage you to refactor and clean up your code.You can also read more about it here https://laravel.com/docs/8.x/eloquent#upserts
NotiMail.php
public $notiInfo;
public function __construct($notiInfo)
{
$this->type = $notiInfo->type;
$this->name = $notiInfo->name;
}
public function build()
{
if($this->type=="Document") {
return $this->markdown('emails.statusDoc')->with('name', $this->name);
}
elseif($this->type=="Announcement") {
return $this->markdown('emails.statusTap')->with('name', $this->name);
}
}
AnnouncementController.php
use Illuminate\Support\Facades\Mail;
use App\Mail\NotiMail;
public function store(Request $request)
{
$hawkers = Hawker::select('hawker_id')->get();
$request->validate([
'announce_title' => ['required', 'unique:announcements,title'],
'announce_desc' => ['required'],
]);
$current = new Carbon('now', 'UTC');
$Mtime = $current->addHours(8);
$announce = new Announcement();
$announce->title = $request->input('announce_title');
$announce->desc = $request->input('announce_desc');
$announce->announce_date = $Mtime;
$announce->save();
foreach ($hawkers as $hawk) {
$noti = new Notification();
$noti->hawker_id = $hawk->hawker_id;
$noti->title = $request->input('announce_title');
$noti->desc = $request->input('announce_desc');
$noti->notiDate = $Mtime;
$noti->save();
$hawker = Hawker::find($hawk->hawker_id);
$notiInfo = [
'name' => $hawker->name,
'type' => "Announcement"
];
Mail::to($hawker->email)->send(new NotiMail($notiInfo));
}
return redirect()->route('announcement.index')->with('success', "Announcement made!");
}
The error appears from NotiMail.php. Most of the solutions where because the public variable was not included. However, as you can see i already have. Yet the array from AnnouncementController does not seem to pass the values to NotiMail.php.
I want to customize column names, concatinate data in one column. Can put some condition on data.
Below is sample image, In which format data would show. How to make possible this-
Here you can find how to manipulate columns names:
https://laravel-excel.maatwebsite.nl/3.1/exports/mapping.html
And below its a example from my project in how to use it. Enjoy!
namespace App\Exports;
use App\Aluno;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithStrictNullComparison;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
class AlunosExport implements FromCollection, WithStrictNullComparison, WithHeadings, ShouldAutoSize
{
/**
* #return \Illuminate\Support\Collection
*/
public function collection()
{
$alunos = Aluno::all();
$dadosAluno = [];
foreach ($alunos as $aluno) {
$textoEtapas = '';
foreach ($aluno->etapas as $etapa) {
$textoEtapas .= "{$etapa->nome}";
if ($etapa->concluido) {
$textoEtapas .= ' (Concluído)';
}
$textoEtapas .= "\n";
}
$dadosAluno[] = [
'nome' => $aluno->cliente->nome,
'telefone' => formatarTelefone($aluno->cliente->telefone),
'instituicao' => $aluno->cliente->turma->unidade->instituicao->nome,
'turma' => $aluno->cliente->turma->nome,
'programa' => $aluno->programa->nome,
'etapas' => $textoEtapas,
'valor' => $aluno->valor,
'orientador' => !is_null($aluno->orientador) ? $aluno->orientador->nome : '(Excluído)',
'status' => $aluno->cliente->status
];
}
$alunoColection = collect($dadosAluno);
return $alunoColection;
}
public function headings(): array
{
return [
'Aluno',
'Telefone',
'Instituição',
'Turma',
'Programa',
'Etapas',
'Valor',
'Orientador',
'Status'
];
}
}
I have a model that has a one to many relationship to the versions of the description.
In my Controller
$tag = Tags::create([
'name' => $request->get('name'),
'user_id' => \Auth::id(),
]);
$tag->update([
'content' => $request->get('description')
]);
In my Model:
public function setContentAttribute(string $value)
{
$this->versions()->create([
'user_id' => \Auth::id(),
'value' => $value
]);
}
So I can't put content directly as an attribute in the create method because there is no Model right now.
But is it possible to overwrite the create Method?
When I try to overwrite something like this in my Model it will do an infinity loop
public static function create($attr) {
return parent::create($attr);
}
So my question is if it is possible to have something like this:
$tag = Tags::create([
'name' => $request->get('name'),
'user_id' => \Auth::id(),
'content' => $request->get('content')
]);
and in the Model:
public static function create($attr) {
$value = $attr['content'];
$attr['content'] = null;
$object = parent::create($attr);
$object->content = $value;
$object->save();
return $object;
}
Update
I didn't overwrite the create method but called it customCreate. So there is no infinity loop anymore and I can pass all variables to the customCreate function that handles the relationships for me.
Solution
After reading the changes from 5.3 to 5.4 it turns out that the create method was moved so you don't have to call parent::create() anymore.
The final solution is:
public static function create($attr) {
$content = $attr['content'];
unset($attr['content']);
$element = static::query()->create($attr);
$element->content = $content;
$element->save();
return $element;
}
I don't see why not and you could probably implement a more general approach? Eg. checking if set{property}Attribute() method exists, if it does - use it to assign a value, if it doesn't - use mass assigning.
Something like:
public static function create($attr) {
$indirect = collect($attr)->filter(function($value, $property) {
return method_exists(self::class, 'set' . camel_case($property) . 'Attribute');
});
$entity = parent::create(array_diff_key($attr, $indirect->toArray()));
$indirect->each(function($value, $property) use ($entity) {
$entity->{$property} = $value;
});
$entity->save();
return $entity;
}
I haven't really tested it but it should work. I use something like this in one of my Symfony apps.
I have a Report Model which is like the following.
class Report extends Model
{
protected $table = 'reports';
protected $guarded = [];
public function leadsCollection()
{
return $this->hasMany('App\ReportModels\LeadsCollection');
}
}
A Report can have many LeadsCollection, its Model is the following.
class LeadsCollection extends Model
{
protected $table = 'leadsCollection';
protected $guarded = [];
private $xmlElement;
public function __construct($xmlElement = null, $attributes = array()) {
parent::__construct($attributes);
$this->xmlElement = $xmlElement;
}
public function report()
{
return $this->belongsTo('App\ReportModels\Report');
}
function asArray(){
$reportItem = array();
foreach($this->xmlElement->Leads->Lead as $lead) {
$dateIdentified = date("d/m/Y", strtotime($lead->Date));
$reportItem[] = array(
'LeadID' => (string)$lead->ID,
'Client' => (string)$lead->Client->Name,
'Category' => (string)$lead->Category,
'DateIdentified' => $dateIdentified,
'LeadName' => (string)$lead->Name,
'Owner' => (string)$lead->Owner->Name
);
}
return $reportItem;
}
}
Now I am trying to save some data to a database. So I get a list of all Leads by calling my LeadsCollection and passing it an XML list of Leads.
I then loop these Leads and add it to an array. At the same time however I need to save it to the database. This is what I have so far.
public function getForecastReportForLeads() {
$leads = new LeadsCollection(new \SimpleXMLElement(Helper::getCurrentLeads()));
$reportArray = array();
foreach ($leads->asArray() as $lead) {
$report = new Report();
$report->reportName = 'Lead Forecast';
if($report->save()) {
$leads->leadId = $lead['LeadID'];
$leads->leadCategory = $lead['Category'];
$leads->dateIdentified = $lead['DateIdentified'];
$leads->leadName = $lead['LeadName'];
$leads->owner = $lead['Owner'];
$leads->client = $lead['Client'];
$leads->report_id = $report->id;
$leads->save();
$reportItem = array(
'leadData' => $lead
);
$reportArray[] = $reportItem;
}
}
return $reportArray;
}
So I create the Report item, and within the database if I have 7 Leads I end up with 7 Report rows within my reports table, as it should be. However, when I save the Leads, I only end up with 1 row in my leadsCollection table, every other entry seems to be overridden. I think this is because I am not creating the Lead Object within the loop. However, I cant really create it within the loop because I need to loop whats returned when I first create it.
Not sure how clear I am but is there anything I can add to my Model so I can stop any overriding? Or do I need to do this another way?
Thanks
Either you get the variable inside the save method or initialize the new
$report = new Report($reportItem);
$report->save($report)
I'm having a similar Issue right, let me show my code. It would work for your case. My bug is that I'm updating and the plan_detail.id gets moved instead of creating a new one. But if you create would be fine:
public function store(Request $request)
{
$this->validate($request, [ 'title' => 'required',
'description' => 'required']);
$input = $request->all();
$plan_details = Plan_Detail::ofUser()->get();
$plan = new Plan($request->all());
DB::beginTransaction();
Auth::user()->plans()->save($plan);
try {
foreach ($plan_details as $k => $plan_detail)
Plan::find($plan['id'])->details()->save($plan_detail);
DB::commit();
} catch (Exception $e) {
Log::error("PGSQL plan detail " . $e->message());
DB::rollback();
session()->flash('message', 'Error al guardar el plan de entreno');
}
return redirect('plans');
}