Laravel Backpack. 1-1 (one-to-one) relation - php

I have 2 Eloquent models:
/**
* Entities/Products.php
*/
use CrudTrait;
protected $fillable = [
'name', 'macronutrients_id',
];
public function macronutrients()
{
return $this->hasOne(Macronutrients::class);
}
/**
* Entities/Macronutrients.php
*/
use CrudTrait;
protected $fillable = [
'proteins', 'fats', 'carbons', 'calories', 'product_id'
];
public function product()
{
return $this->belongsTo(Product::class);
}
I don't know how I can show table (or something like list of options) with all macronutrients on product's edit page via Laravel Backpack CRUD?
In other words, I want to make something like this:
on page http://example.com/admin/product/2/edit:
* [text] Name
* Macronutrients:
[number] proteins
[number] fats
[number] carbons
[number] calories
where [text], [number] is input fields.

I resolved this with some custom logic. As a result:
Screenshot of my /admin/product/1/edit
First of all, I created custom field:
<!-- /resources/views/vendor/backpack/crud/fields/product_macronutrients.blade.php -->
<!-- product_macronutrients -->
#php($macronutrients = isset($entry) ? $entry->macronutrients : false)
<div #include('crud::inc.field_wrapper_attributes') >
#include('crud::inc.field_translatable_icon')
<div class="array-container form-group">
<table class="table table-bordered table-striped m-b-0">
<thead>
<tr>
<th class="text-center">{{-- <i class="fa fa-trash"></i>--}} </th>
#foreach( $field['columns'] as $column )
<th style="font-weight: 300!important;">
// l10n strings (productscatalog::labels.proteins, productscatalog::labels.fats and so on)
#lang("productscatalog::labels.$column")
</th>
#endforeach
</tr>
</thead>
<tbody ui-sortable="sortableOptions" class="table-striped">
<tr class="array-row">
<td>
<p><b>#lang("productscatalog::labels.macrontr")</b></p>
</td>
#foreach( $field['columns'] as $column)
<td>
<input
class="form-control input-sm"
type="text"
name="{{ $column }}"
value="{{ old($column) ? old($column) : $macronutrients ? $macronutrients->$column : '' }}"
#include('crud::inc.field_attributes')
/>
</td>
#endforeach
</tr>
</tbody>
</table>
</div>
</div>
And ProductCrudController:
public function setup()
{
// other stuff...
$this->crud->addField([
'label' => 'Macronutrients',
'type' => 'product_macronutrients',
'name' => '',
'columns' => [
'proteins',
'fats',
'carbons',
'calories',
],
]);
}
public function store(StoreRequest $request)
{
$redirect_location = parent::storeCrud($request);
$this->storeOrUpdateMacronutrients($request, $this->crud->entry);
return $redirect_location;
}
public function update(UpdateRequest $request)
{
$redirect_location = parent::updateCrud($request);
$this->storeOrUpdateMacronutrients($request, $this->crud->entry);
return $redirect_location;
}
public function destroy($id)
{
$this->destroyMacronutrients($id);
$return = parent::destroy($id);
return $return;
}
protected function storeOrUpdateMacronutrients(Request $request, Product $product)
{
$macronutrients = Macronutrients::firstOrNew(['id' => $product->id]);
$macronutrients->proteins = $request->input('proteins');
$macronutrients->fats = $request->input('fats');
$macronutrients->carbons = $request->input('carbons');
$macronutrients->calories = $request->input('calories');
$macronutrients->save();
}
protected function destroyMacronutrients($productId)
{
$macronutrients = Macronutrients::findOrFail($productId);
$macronutrients->delete();
}
Hope it helps.

$this->crud->addColumn([
// 1-n relationship
'label' => "Country name", // Table column heading
'type' => "select",
'name' => 'country_name', // the column that contains the ID of that connected entity;
'entity' => 'country', // the method that defines the relationship in your Model
'attribute' => "country_name", // foreign key attribute that is shown to user
'model' => "App\Models\Country",
]);
this is an example for 1-n relationship in laravel backpack

Related

How to save ID dropdown's attributes dynamic in laravel livewire component

How can I save the ID (primary key) of the dynamic dropdowns in a combined mysql field (1,3,2,1 etc... )
I'm sorry, my english is bad
see image form in blade
field mysql save: combinated varchar(255)
see table price mysql with field for save
View Blade:
<form class="form-horizontal" wire:submit.prevent="storePrecio">
#csrf
#foreach ($product->attributeValues->unique('product_attribute_id') as $av)
<div class="form-group">
<label class="col-md-4 control-label">{{ $av->productAttribute->name }} :</label>
<div class="col-md-4">
<select class="form-control" wire:model="attribute_values.{{ $av->productAttribute->id }}">
#foreach ($av->productAttribute->attributeValues->where('product_id', $product->id) as $pav)
<option value="{{ $pav->id }}">{{ $pav->value }}</option>
#endforeach
</select>
</div>
Component Livewire:
class AdminAddPrecioComponent extends Component
{
// tabla precios
public $product_id;
public $category_id;
public $combination;
public $size;
public $quantity;
public $importe;
//atributos de producto
public $attribute_values = [];
public function storePrecio()
{
$this->validate([
'size' => 'required',
'quantity' => 'required',
'importe' => 'required'
]);
//grabar
$product = Product::where('slug', $this->slug)->first();
$product_id = $product->id;
$category_id = $product->category_id;
//
$precio = new Price();
$precio->category_id = $category_id;
$precio->product_id = $product_id;
$precio->size = $this->size;
$precio->quantity = $this->quantity;
$precio->importe = $this->importe;
//saving ID dropdowns dynamic array
foreach ($this->attribute_values as $key => $attribute_value[]) {
$avalues = explode(",", $attribute_value[]);
foreach ($avalues as $avalue) {
$precio->combination = $avalue[$key]; //NOT WORKING => EMPTY
}
}
$precio->save();
session()->flash('message', 'Precio ha sido creado con exito');
}
public function render()
{
$tamano = Tamano::all();
$cantidad = Cantidad::all();
$product_array = Product::all();
$categoria = Category::all();
$product = Product::where('slug', $this->slug)->first();
return view('livewire.admin.admin-add-precio-component', ['cantidad' => $cantidad, 'tamano' => $tamano, 'product_array' => $product_array, 'product' => $product, 'categoria' => $categoria])->layout('layouts.frontend.base');
}
my table attributes fill dropdowns
see table here

Laravel 8.x - pass pivot table data to mailable

I have a platform where students can apply for academic courses. When an application is sent, apart from being saved into the database, it must fire an email with the information of the applicant along with the titles of the applied courses.
An Applicant can apply for many Courses, hence the pivot table Applicant_Course. The Applicant model:
class Applicant extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'fname',
'lname',
'email',
'phone',
'is_student',
];
public function courses()
{
return $this->belongsToMany(Course::class)->withTimestamps();
}
The Mail\ApplicationMail:
public function __construct(Applicant $applicant)
{
$this->applicant = $applicant;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
// return $this->view('view.name');
}
The problem is that I cannot figure out how to pass the Applicant object into the mailable. This is what the applicationMail view looks like:
<tr class="email-row">
<td class="left-column"><b>Email: </b></td>
<td class="right-column email-cell">
<a href="{{ $email }}">
{{ $email }}
</a>
</td>
</tr>
#isset($phone)
<tr>
<td class="left-column"><b>Phone: </b></td>
<td class="right-column">
<td class="right-column subject-text">{{ $phone }}</td>
</td>
</tr>
#endisset
<tr>
#foreach ($applicant->courses as $course)
<div class="application-body-item">
<p>{{$course->title}}</p>
<p>Course Code: {{$course->code}}</p>
<hr>
</div>
#endforeach
</tr>
But the line where it says: '#foreach ($applicant->courses as $course)' returns the error: 'Undefined variable: applicant'.
Here is my ApplicationsController:
$input = $request->all();
\Mail::send('applicationMail', array(
'title' => $input['title'],
'org' => $input['org'],
'fname' => $input['fname'],
'lname' => $input['lname'],
'email' => $input['email'],
'course-check' => $input['course-check'],
'phone' => $input['phone'],
'created_at' => $input['created_at'],
'updated_at' => $input['updated_at'],
), function($message) use ($request){
$message->from(env('MAIL_USERNAME'));
$message->to(env('MAIL_USERNAME'))->subject($request->get('subject'));
});
Any idea how to pass the Applicant's data into the mailable so that I can retrieve the pivot table data (from the Applicant_Course table) just like I do inside a normal view ?

Laravel Model binding many to many realtionship does not populate data

I have two tables with a many to many relation (Project and Center, the pivot table is ProjectCenter).
These are my models:
Project:
class Project extends Model {
public function centers()
{
return $this->belongsToMany('App\Models\Center', 'ProjectCenter', 'IDProject', 'IDCenter');
}
public function getCenterListAttribute()
{
return $this->centers->lists('IDCenter')->all();
}
}
Center:
class Center extends Model {
public function projects()
{
return $this->belongsToMany('App\Models\Project', 'ProjectCenter', 'IDCenter', 'IDProject');
}
}
Controller -> edit:
public function edit($id)
{
$project = Project::find($id);
$centerList = Center::lists('Name', 'IDCenter')->toArray();
return view('project/add', array('centerList' => $centerList))->with('project', $project);
}
And the view:
{!! Form::label('centers_list', 'Center*') !!}
{!! Form::select('centers_list[]',
$centerList,
null,
array(
'class' => 'form-control ',
'required' => 'required',
'multiple' => true,
'data-placeholder' =>
'Select a center'
)
) !!}
But I can not select the data already stored previously.
For example: the project 8 (IDProject) has two centers (1 and 2) but the data is not populated in the multiple select:
What am I doing wrong?
You all time get same result $centerList = Center::lists('Name', 'IDCenter')->toArray(); but you must be get product centers using query with model.
$project = Project::with("centers:Name,IDCenter")->find($id);
$centerList = $project->centers->pluck('Name', 'IDCenter')->toArray();
I already solve the problem using a foreach to select the related centers:
<select multiple="multiple" name="centers[]" id="centers" class="form-control select2" required="required" data-placeholder="Select a center">
#if($centerList)
#foreach($centerList as $key => $center)
<option value="{{$key}}" {{ (collect($selectedCenters)->contains($key)) ? "selected='selected'" : '' }} >{{$center}}</option>
#endforeach
#endif
</select>

How can I display 4 different data on each tables to be seen on a one table named Transaction in Laravel?

I am newbie on Laravel and exploring more to learn. I am working on a project which is a Video store. It has 5 tables:
Customer (customer_id, name, address)
Category (category_id, name, description, price)
Videos (video_id, title, description, category_id , stock)
Rental (customer_id, rented_at, total_amount, interest_amount, status)
Transaction (video_id, price, transaction_id, quantity, status, returned_at [date])
I want to display in the Rental Table the Title of the Video with the corresponding Category Name and Price.
This is my code for RentalController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Input;
use App\Http\Requests;
use Carbon\Carbon;
use App\Transaction;
use App\Customer;
use App\Category;
use App\Rental;
use App\Video;
use DB;
class RentalController extends Controller {
public function __construct(){
$this->middleware('auth');
}
public function index(){
$rentals = Rental::all();
return view('rental.index', compact('rentals'));
}
public function create(){
$rental = new Rental;
$customers = Customer::pluck('name','id');
$videos = Video::pluck('title','id');
$categories = Category::pluck('price', 'id');
return view('rental.create', compact('rental', 'customers', 'videos', 'categories'));
}
public function store(Request $request) {
$this->validate($request,[
'videos.*.id' => 'required'
]);
$data = $request->all();
DB::transaction(function() use ($data) {
$rental = new Rental;
$rental->customer_id = $data['customer_id'];
$rental->rented_at = Carbon::now();
$rental->interest_amount = 0;
$rental->status = 'rented';
dd($data['videos']);
$rental->save();
foreach ($data ['videos'] as $video) {
$detail = new Transaction;
$detail->video_id = $video['id'];
$detail->rental_id = $rental->id;
$detail->quantity = 1;
$detail->status = 'rented';
$detail->price = Video::find($video['id'])->category->price;
$detail->save;
$total_amount = $detail->price;
}
$rental->total_amount = $total_amount;
$rental->save();
});
flash('Request Successfully Saved', 'success');
return redirect()->action('RentalController#index');
}
public function edit(Rental $rental) {
$customers = Customer::pluck('name','id');
$videos = Video::pluck('title','id');
$categories = Category::pluck('price', 'id');
return view('rental.edit',compact('rental', 'customers', 'videos', 'categories'));
}
public function update(Request $request, Rental $rental) {
$data = $request->all();
$rental->customer_id = $data['customer_id'];
$rental->rented_at = $data['rented_at'];
$rental->total_amount = $data['total_amount'];
$rental->interest_amount = $data['interest_amount'];
$rental->status = $data['status'];
$rental->save();
flash('Request Successfully Updated', 'success');
return redirect()->action('RentalController#index');
}
public function destroy(Rental $rental) {
$rental->delete();
flash('Request Successfully Deleted', 'success');
return redirect()->action('RentalController#index');
}
public function show(Rental $rental) {
return view('rental.show', compact('rental'));
}
}
create.blade.php
#extends('layouts.app')
#section('content')
<div class="row">
<h1 class = "page-header">Rent a Video</h1>
<div class="col-md-12">
{!! Form::model($rental, ['action' => 'RentalController#store', 'method' => 'POST', 'class' => 'form']) !!}
#include('rental.form')
{!! Form:: close() !!}
</div>
</div> #endsection
form.create.php
<div class= "form-group">
{!! Form::label('customer_id', 'Customer'); !!}
{!! Form::select('customer_id', $customers , null, ['class' => 'form-control','placeholder'=>'Customer Name']); !!}
</div>
<div class= "form-group">
{!! Form::label('total_amount', 'Total Amount'); !!}
{!! Form::text('total_amount', null, ['class' => 'form-control']); !!}
</div>
<table class="table table-bordered">
<thead>
<tr>
<th>Title</th>
<th>Category</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>{!! Form::select('videos[1][id]', $videos, null, ['class' => 'form-control', 'placeholder' => 'Choose a video']); !!}</td>
<td></td>
<td><</td>
</tr>
<tr>
<td>{!! Form::select('videos[1][id]', $videos, null, ['class' => 'form-control', 'placeholder' => 'Choose a video']); !!}</td>
<td></td>
<td></td>
</tr>
<tr>
<td>{!! Form::select('videos[2][id]', $videos, null, ['class' => 'form-control', 'placeholder' => 'Choose a video']); !!}</td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
{!! Form::submit('Save', ['class' => 'btn btn-success']); !!}
Ok first and foremost, the structure of your database should be like:
Customer (customer_id, name, address)
public function rentals()
{
return $this->hasMany('App\Rental');
}
Category (category_id, name, description, price)
public function videos()
{
return $this->hasMany('App\Video');
}
Videos (video_id, title, description, category_id , stock)
public function category()
{
return $this->belongsTo('App\Category');
}
Rental (rental_id, customer_id, rented_at, total_amount, interest_amount, status)
public function customer()
{
return $this->belongsTo('App\Customer');
}
public function transactions()
{
return $this->hasMany('App\Transaction');
}
Transaction (transaction_id, rental_id, price, video_id, quantity, status, returned_at [date])
public function rental()
{
return $this->belongsTo('App\Rental');
}
public function video()
{
return $this->belongsTo('App\Video');
}
Explained: Customer is the peak of the hierarchy , having only many Rental as its children .. Rental as being a child of Customer we declare that it belongs to a Customer .. Also having one Transaction .. Transaction belongs to a rental, as well a video.. then Video belongsTo a Category and so Category hasMany videos ..
now, on how to show data from this is:
view rentals by user
$customers = Customers::all();
foreach($customers as $customer)
{
echo '<h1>'.$customer->name .'</h1>';
echo '<h3>Rentals</h3>';
foreach($customer->rentals as $rental)
{
echo $rental->total_amount;
// and so on ..
foreach($rental->transactions as $transaction)
{
echo $transaction->video->title;
// and so on ..
echo $transaction->video->category->price;
}
}
}
Its better you go for mysql view.
Create a mysql view from a join query and use that view as Model in Laravel.
Please note. Views can only be used to read data i.e. for select queries only

Yii2 save array of form fields to a single database field

How can I save an array of fields in Yii2 the current/default setup only works for field which aren't array.
Below are the form fields I need to save into the single field:
<div class="repeat">
<table class="wrapper" width="100%">
<thead>
<tr>
<td width="10%" colspan="4"><span class="add">Add</span></td>
</tr>
</thead>
<tbody class="container">
<tr class="template row">
<td width="10%"><span class="move">Move</span></td>
<td width="10%">An Input Field</td>
<td width="70%">
<?= $form->field($model, 'field1ofarray[{{row-count-placeholder}}]')->textInput(['maxlength' => 255])->label('Field Label') ?>
<?= $form->field($model, 'fieldofarray[{{row-count-placeholder}}]')->textInput(['maxlength' => 255])->label('Som field') ?>
<?= $form->field($model, 'field3ofarray[{{row-count-placeholder}}]')->textInput(['maxlength' => 255])->label('Field Label') ?>
<?= $form->field($model, 'field4ofarray[{{row-count-placeholder}}]')->dropDownList(['instock' => 'Instock', 'soldout' => 'Sold Out', 'scheduled' => 'Scheduled']); ?>
</td>
<td width="10%"><span class="remove">Remove</span></td>
</tr>
</tbody>
</table>
Current Controller (I need to know how I can loop through array and save as well as saving other normal fields in my form):
public function actionCreate()
{
$model = new GrailWall();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
In my case I didn't need to make any changes to the controller at all.
You can just make a field in your db record, something like 'config_json` and then define a virtual property with getter and setter in your model.
public function getConfig()
{
return json_decode($this->config_json);
}
public function setConfig($value)
{
$this->config_json = json_encode($value);
}
Also set your virtual property to be safe in the rules so Massive Assignment works.
public function rules()
{
return [
[['company_id', 'created_at', 'updated_at'], 'integer'],
[['class'], 'required'],
[['config_json'], 'string'],
[['class'], 'string', 'max' => 255],
[['config'], 'safe']
];
}
Now you can set inputs like this in your view
<?= $form->field($model, 'config[ga_id]', ['labelOptions' => ['label' => 'Google Analytics Tracking ID']])->textInput(['maxlength' => true]) ?>
you can use loadMultiple(), look at this
this is about tabular/multiple input.
Otherwise load the modal you need using load($_POST[your_form_name].
models
public function getNetworkTypeArray()
{
return explode(',', $this->network_type);
}
public function setNetworkTypeArray(array $value)
{
$this->network_type = implode(',', $value);
}
controller
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post())) {
$model->setNetworkTypeArray($model->network_type);
if ($model->save()) {
return $this->redirect(['index']);
}
}
$model->network_type = $model->getNetworkTypeArray();
return $this->render('update', [
'model' => $model,
]);
}
$form->field($form, 'email[]')->textInput(['id' => 'email-0']);
will render:
<input type="text" name="Form[email][]" id="email-0">
Please note double square brackets in ($form, 'email[]').
ID attribute must be set explicitly unique, otherwise duplicate IDs will render, which does not conform with W3C/javascript standards.
instead saving in array you can save in string, to that use tne variables as normal in the view, ex:
fieldofarray instead fieldofarray[],
and in the controller use the implode function, ex:
public function actionCreate()
{
$model = new someModel();
if ($model->load(Yii::$app->request->post())) {
$model->fieldofarray = implode (",",$model->fieldofarray);
$model->save();
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}

Categories