Laravel update with relationship - laravel

I try to update a component that has a relationship, from frontend I send the correct data, but in back works to update only the main table.
public function updateCustomer(Request $request, Customer $customer)
{
$customer->$customerNames->update();
$customer->update($request->all());
return response($customer, 200);
}

You may use
$customer->account_name = $request->account_name;
$customer->sage_id = $request->sage_id;
$customer->update();
$customerNames = [];
foreach($request->customer_names as $customer_name) {
$customerNames[] = new CustomerName(
[
'name' => $customer_name['name'],
'name_type' => $customer_name['name_type'],
'movation_date' => customer_name['movation_date']
]
);
}
$customer->customerNames()->delete();
$customer->customerNames()->saveMany($customerNames);
//...

Related

Laravel - How to update Input Array without deleting Sales Detail

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

Laravel / OctoberCMS frontend filter

I am using OctoberCMS and I have created a custom component. I am trying to create a frontend filter to filter Packages by the Tour they are assigned to.
This is what I have so far. The issue is that the code is looking for a tour field within the packages table rather than using the tour relationship. Does anyone have any ideas?
<?php namespace Jakefeeley\Sghsportingevents\Components;
use Cms\Classes\ComponentBase;
use JakeFeeley\SghSportingEvents\Models\Package;
use Illuminate\Support\Facades\Input;
class FilterPackages extends ComponentBase
{
public function componentDetails()
{
return [
'name' => 'Filter Packages',
'description' => 'Displays filters for packages'
];
}
public function onRun() {
$this->packages = $this->filterPackages();
}
protected function filterPackages() {
$tour = Input::get('tour');
$query = Package::all();
if($tour){
$query = Package::where('tour', '=', $tour)->get();
}
return $query;
}
public $packages;
}
I really appreciate any help you can provide.
Try to query the relationship when the filter input is provided.
This is one way to do it;
public $packages;
protected $tourCode;
public function init()
{
$this->tourCode = trim(post('tour', '')); // or input()
$this->packages = $this->loadPackages();
}
private function loadPackages()
{
$query = PackagesModel::query();
// Run your query only when the input 'tour' is present.
// This assumes the 'tours' db table has a column named 'code'
$query->when(!empty($this->tourCode), function ($q){
return $q->whereHas('tour', function ($qq) {
$qq->whereCode($this->tourCode);
});
});
return $query->get();
}
If you need to support pagination, sorting and any additional filters you can just add their properties like above. e.g;
protected $sortOrder;
public function defineProperties(): array
{
return [
'sortOrder' => [
'title' => 'Sort by',
'type' => 'dropdown',
'default' => 'id asc',
'options' => [...], // allowed sorting options
],
];
}
public function init()
{
$filters = (array) post();
$this->tourCode = isset($filters['tour']) ? trim($filters['tour']) : '';
$this->sortOrder = isset($filters['sortOrder']) ? $filters['sortOrder'] : $this->property('sortOrder');
$this->packages = $this->loadPackages();
}
If you have a more complex situation like ajax filter forms or dynamic partials then you can organize it in a way to load the records on demand vs on every request.e.g;
public function onRun()
{
$this->packages = $this->loadPackages();
}
public function onFilter()
{
if (request()->ajax()) {
try {
return [
"#target-container" => $this->renderPartial("#packages",
[
'packages' => $this->loadPackages()
]
),
];
} catch (Exception $ex) {
throw $ex;
}
}
return false;
}
// call component-name::onFilter from your partials..
You are looking for the whereHas method. You can find about here in the docs. I am not sure what your input is getting. This will also return a collection and not singular record. Use ->first() instead of ->get() if you are only expecting one result.
$package = Package::whereHas('tour', function ($query) {
$query->where('id', $tour);
})->get();

Is there any efficient method on how to get id of object to my create method

I am creating a web module, and want to get ID of table licensing level two parse into my create method. Hence each ID of level will have a task and the ID need to be stored within my licensing table as a foreign key which reflects ID in Level Two table. How could I solve this, anyone can give me a good suggestion or way on doing this
public function add_show($id)
{
$level = PreLicensingLevelTwo::where('id', $id)->first();
$level->prelicensingtask = PreLicensingTask::where('pre_licensing_level_two_id', $level->id)->with('staff', 'statusdesc', 'prelicensingtaskstaff')->get();
return view('staff.regulatory.statutory.approval.display',compact('level'));
}
public function create()
{
$staff = Staff::pluck('staff_name');
$status = PreLicensingStatus::pluck('status_description', 'id');
return view('staff.regulatory.statutory.approval.create', compact('staff','status'));
}
public function show($id)
{
$one = PreLicensingLevelOne::where('pre_licensing_main_id', $id)->get();
foreach ($one as $key => $license)
{
$license->two = PreLicensingLevelTwo::where('pre_licensing_level_one_id', $license->id)->get();
}
$rendered = view('staff.regulatory.statutory.approval.show')->with('one', $one)->render();
return response()->json(array('status' => 1, 'tableData' => $rendered));
}
With help from my working collegue this is how i able to solve the question i asked
public function store(Request $request)
{
$this->validate($request, [
'task_title' => 'required',
'task_description' => 'required',
'task_due_date' => 'required',
]);
$leveltwo = PreLicensingLevelTwo::find($request->input('pre_licensing_level_two_id'));
$prelicensingtask = new PreLicensingTask;
$prelicensingtask->task_title =$request->input('task_title');
$prelicensingtask->task_description =$request->input('task_description');
$prelicensingtask->task_due_date =$request->input('task_due_date');
$prelicensingtask->created_by_staff_id = Auth::user()->ref_user->staff_id;
$prelicensingtask->status = $request->input('status');
$prelicensingtask->pre_licensing_level_two_id = $leveltwo->id;
$prelicensingtask->pre_licensing_level_one_id = $leveltwo->pre_licensing_level_one_id;
$prelicensingtask->pre_licensing_main_id = $leveltwo->pre_licensing_main_id;
$prelicensingtask->centre_id = Auth::user()->ref_user->centre_id;
$prelicensingtask->save();
return redirect()->back();
}

new created model with related table output and then to json

How do get the related user table of a newly created Contact model and then in the response header content-length out put it toJson().
public function store(Request $request) {
try {
$contact = new Contact();
$contact->email_address = Helper::strip_tags($request->get('email_address'));
$contact->firstname = ucfirst($request->get('firstname'));
$contact->lastname = ucfirst($request->get('lastname'));
$contact->company = ucfirst($request->get('company'));
$contact->phone = $request->get('phone');
$contact->mobile = $request->get('mobile');
$contact->description = Helper::strip_tags($request->get('description'));
if($contact->save()) {
// here is the part I'm having trouble with
$contact = $contact->with('user')->get();
return response()->json($contact, 200, ['Content-Length' => strlen($contact->toJson())]);
} else {
return response()->json(array('error' => true, 'messages' => $contact->errors), 400);
}
} catch(Exception $e) {
return response()->json(array('error' => true, 'type' => 'exception', 'message' => $e->getMessage()), 500, ['Content-Length' => $e->getMessage()]);
}
As you already have the model loaded (when you created it) you wouldn't use with() as it is for eager loading relationships.
If I understand you question correctly, to get the User relationship included in the output you would instead use lazy eager loading which would look like:
$contact->load('user');

Symfony3 error changing DateTime on entity with Ajax

I want to change the date of a doctrine entity but the change is not saved.
With ajax a call this function:
public function relancerTicketAction(Request $request, $id)
{
if (!$this->get('session')->get('compte'))
return $this->redirect($this->generateUrl('accueil'));
$isAjax = $request->isXMLHttpRequest();
if ($isAjax)
{
$ticket = $this->getDoctrine()->getManager()->getRepository('CommonBundle:Ticket')->find($id);
$ticket->setDateButoire($ticket->getDateButoire()->modify('+7 day'));
$this->getDoctrine()->getManager()->flush();
$response = array("code" => 100, "success" => true, 'date' => $ticket->getDateButoire()->format('d-m-Y'));
return new Response(json_encode($response));
}
$response = array("code" => 0, "success" => false);
return new Response(json_encode($response));
}
When I alert the result I get the right new value, but after reload there is no change saved.
This function called in the same conditions works:
public function traiterTicketAction(Request $request, $id)
{
if (!$this->get('session')->get('compte'))
return $this->redirect($this->generateUrl('accueil'));
$isAjax = $request->isXMLHttpRequest();
if ($isAjax)
{
$compte = $this->getDoctrine()->getManager()->getRepository('CommonBundle:Compte')->find($this->get('session')->get('compte')->getId());
$ticket = $this->getDoctrine()->getManager()->getRepository('CommonBundle:Ticket')->find($id);
$ticket->addDestinataire($compte);
$this->getDoctrine()->getManager()->flush();
$response = array("code" => 100, "success" => true);
return new Response(json_encode($response));
}
$response = array("code" => 0, "success" => false);
return new Response(json_encode($response));
}
see the docs
When calling EntityManager#flush() Doctrine computes the changesets of
all the currently managed entities and saves the differences to the
database. In case of object properties (#Column(type=”datetime”) or
#Column(type=”object”)) these comparisons are always made BY
REFERENCE. That means the following change will NOT be saved into the
database:
/** #Entity */
class Article
{
/** #Column(type="datetime") */
private $updated;
public function setUpdated()
{
// will NOT be saved in the database
$this->updated->modify("now");
}
}
So, in your case I suggest to clone dateButoire, like this
$ticket = $this->getDoctrine()->getManager()->getRepository('CommonBundle:Ticket')->find($id);
$newDateButoire = clone $ticket->getDateButoire();
$ticket->setDateButoire($newDateButoire->modify('+7 day'));
$this->getDoctrine()->getManager()->flush();

Resources