This question already has answers here:
getting the value of an extra pivot table column laravel
(3 answers)
Closed 4 years ago.
I have two models: Event and Ticket. I assigned between them Many-To-Many relationship: Here it is:
Ticket model:
public function events()
{
return $this->belongsToMany('App\Event');
}
Event model:
public function tickets()
{
return $this->belongsToMany('App\Ticket');
}
And then I in my Controller I want to update pivot table data:
public function register(Request $request)
{
$event = Event::findOrFail($request->event);
if(count($event) > 0) {
foreach($request->tickets_type as $key => $value) {
if($value !== null) {
for($i = 0; $i < (int) $value; $i++) {
$sale = new Sale;
$sale->event_id = $request->event;
$sale->event_type = $request->event_type;
$sale->user_id = Auth::user()->id;
$sale->ticket_type = $key;
$sale->save();
$sale->qr = $this->generateQR($sale->event_id, $sale->ticket_type, $sale->id);
$sale->update();
}
$event->tickets->pivot->ticket_quantity -= $value;
}
}
return response()->json([
'success' => true,
'message' => 'Покупка билетов успешно завершена.',
'data' => $request->all()
], 200);
} else {
return response()->json([
'success' => false,
'error' => 'Попытка подмены данных.'
], 403);
}
}
On $event->tickets->pivot->ticket_quantity -= $value; it throws me an error:
Property [pivot] does not exist on this collection instance.
What's the workaround?
change this
$event->tickets->pivot->ticket_quantity -= $value;
to this
$vent->tickets()->attach(id, ['ticket_quantity' => $value]);
You can also use sync which deletes the extra entries which are not in your input
$vent->tickets()->sync(id, ['ticket_quantity' => $value]);
Hope this helps
Related
Currently learning Laravel and any help is much appreciated!
My API controller has the following index function
public function index()
{
abort_if(Gate::denies('course_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');
$response=Course::all()->toArray();
$allData = [];
foreach (Course::all() as $ids=>$CMF) {
UNSET($response[$ids]['media']);
$data_sequence = DB::table('media_sequence')->where('data_id', $CMF["id"])->where('type','CMF')->first();
$data_id=$data_sequence->id;
$data_sequence = json_decode($data_sequence->data_sequence);
$data = [];
$data["id"] = $CMF["id"];
$data["title"] = $CMF["title"];
foreach ($data_sequence as $id => $dataSeq) {
if ($dataSeq->type == "Text") {
$response[$ids]['media'][]=["id"=>$data_id,"text"=> $dataSeq->name,"mime_type"=>"text"];
} elseif ($dataSeq->type == "file") {
foreach ($CMF["media"] as $file) {
if (str::slug($dataSeq->name) == str::slug($file["file_name"])) {
$file["thumb"] = $file->getUrl('video_thumb');
$response[$ids]['media'][]=$file;
}
}
}
}
$allData[] = $data;
}
return new CourseResource($response);
//Commented: return new CourseResource(Course::with(['category', 'assigned_teams', 'team'])->get());
}
Getting no result when trying to return 'assigned_teams' with $response
The API response still doesn't include 'assigned_teams'
I tried: return new CourseResource($response, 'assigned_teams');
It is not returning the assigned_items since it is not included in the $response array.
Change
$response=Course::all()->toArray();
To
$response=Course::with(['category', 'assigned_teams', 'team'])->get();
Read more: eager-loading-multiple-relationships
Btw, as #apokryfos mentioned, you should refactor your code using Eloquent Relationships and Eager Loading.
I assume that the assigned_teams are not handled in your CourseResource.
You need to extend your resource to respect this additional relation.
class CourseResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
// return teams if they have been loaded
'teams' => TeamsResource::collection($this->whenLoaded('assigned_teams')),
];
}
}
This is just exemplary, since you did not provided your code for CourceResource yet, you need to update it according to your needs.
Here is the link to the appropriate laravel documentation: https://laravel.com/docs/8.x/eloquent-resources#conditional-relationships
I'm having one more problem in the logical realm.
I have an array containing ids:
$product_ids = ['1', '5', '3']; <- Example
Another string that I convert to an array separating it by commas, this to indicate the quantities of the products to be withdrawn. For example for product 1 I want 3 drives so I would need the array element to be "1,3".
$finalarray = ["1,3", "5,2", "3,10"];
Next I indicate the code that is executed in the controller (where is what I previously told):
public function ordenform(Request $request)
{
$input = $request->all();
$request->validate([
'nombre' => 'required|string',
'apellido' => 'required|string',
'productos' => 'required',
'cantidades' => 'required|string',
'enviosino' => 'required|in:si,no',
]);
// Quantity Array
$cantidades = explode(',', $input['cantidades']);
if (count($cantidades) != count($input['productos'])) {
return back()->withErrors(['Las cantidades no corresponden a los productos agregados']);
}
$total = 0;
$ganancia = 0;
foreach ($input['productos'] as $producto) {
$producto = Product::find((int) $producto);
$total += $producto->value;
$ganancia += $producto->ganancia;
$producto->stock = (int) $producto->stock - 1;
$producto->save();
}
if ($input['enviosino'] == 'si') {
$total += (int) $input['envio'];
}
if ($input['envio'] == null) {
$input['envio'] = 0;
}
// Products IDS Array
$jsonproductos = json_encode($input['productos']);
Order::create([
'nombre' => $input['nombre'],
'apellido' => $input['apellido'],
'product_ids' => $finalprods,
'value' => $total,
'ganancia' => $ganancia,
'envio' => $input['envio'],
]);
$caja = Config::where('id', 1)->get()->first();
$caja->dinerototal += $total;
$caja->gananciatotal += $ganancia;
$caja->save();
return back()->with('success', 'Orden creada correctamente');
}
Finally, I need to pass as creation parameter the final array to the column products_ids (later I will modify the name).
Another option I thought of is passing objects to an array:
[{id: 1, quantity: 3}]
But I don't know how to get to create that object, I'm still kind of new hehe.
I hope I have explained myself well, English is not my native language. I'm sorry.
I am attentive to your comments !! Greetings.
PS: I am using Laravel
To achieve [{id: 1, quantity: 3}] there will be several idea, but it seems to be an arrayList, so below is how you can create an arrayList in PHP. I have not tested the code, just written here, but should give you the idea to achieve this.
I am considering one is Order class.
<?php
class Order {
private $id;
private $quantity;
public function setId(int $id) {
$this->id = $id;
return $this;
}
public function getId(){
return $this->id;
}
public function setQuantity(int $quantity) {
$this->quantity = $quantity;
return $this;
}
public function getQuantity(){
return $this-> quantity;
}
public function toArray(){
return [
'id' => $this->getId(),
'quantity' => $this->getQuantity()
];
}
}
?>
another is OrderList.
<?php
class OrderList {
private $orderList;
public function __construct(Order ...$orders) {
$this->orderList = $orders;
}
public function toArray(){
$arr = [];
foreach ($this->orderList as $order) {
$arr[] = $order->toArray();
}
return $arr;
}
}
?>
and then use like
$order1 = new Order();
$order1 = $order1->setId(1)->setQuantity(10);
$order2 = new Order();
$order2 = $order1->setId(2)->setQuantity(20);
$orderList = new OrderList($order1, $order2);
var_dump(json_encode($orderList->toArray()));
//Output
string(47) "[{"id":2,"quantity":20},{"id":2,"quantity":20}]"
You do not need json_encode, I have added it to print only.
Nevermind, resolved ->
$prodsfinal = array();
for ($i = 0; $i < sizeof($cantidades); $i++) {
$array = [
'id' => json_decode($input['productos'][$i]),
'cantidad' => (int) $cantidades[$i],
];
array_push($prodsfinal, $array);
}
I need to transfer data from Mysq table (Paniers) to another Mysql table (Commandes) and delete the data from first table after transfer.
Here is my code:
function Commande(Request $request) {
$pn = $request->input('id');
$pdr = Panier::find($pn);
$user = Commande::create([
'ID_User' => $pdr->ID_User,
'ID_Piece' => $pdr->ID_Piece,
'QTE' => $pdr->QTE,
]);
if($user){
if($pdr->delete())
{
echo 'Commande Confirmée';
}
}
}
I get this error:
"Property [ID_User] does not exist on this collection instance."
If i do this it works but instead of getting all data i only get the first line. I need to get all lines of data!
$pdr = Panier::find($pn)->first();
If $pn is array Panier::find($pn) returns collection not entity so you should iterate it
Panier::find($pn)->each(function($pdr){
$user = Commande::create([
'ID_User' => $pdr->ID_User,
'ID_Piece' => $pdr->ID_Piece,
'QTE' => $pdr->QTE,
]);
if($user){
if($pdr->delete())
{
echo 'Commande Confirmée';
}
}
});
When you are doing :
$pdr = Panier::find($pn);
If the record does not exist, it will return null. Then if you do $pdr->ID_User it is going to throw an error. Please check beloew updates :
<?php
function Commande(Request $request) {
$pn = $request->input('id');
$pdr = Panier::find($pn);
// Model not found
if(!$pdr){
return response()->json(['msg' => 'No records found']);
}
// Create new Commande
$user = Commande::create([
'ID_User' => $pdr->ID_User ?? 'default_value_for_ID_User',
'ID_Piece' => $pdr->ID_Piece ?? 'default_value_for_ID_Piece',
'QTE' => $pdr->QTE ?? 'default_value_for_QTE'
]);
// If user is created
if($user){
// Delete Panier
$pdr->delete();
return response()->json(['msg' => 'Success']);
}
return response()->json(['msg' => 'Could not create new Commande']);
}
For above to work you need to have :
Columns ID_User, ID_Piece and QTE marked as $fillable = [] in Commande Model.
You need to have a basic primary key for Panier Model, otherwise delete() will not work.
You can do it by findOrFail and handling Exception:
function Commande(Request $request) {
$pn = (int) $request->input('id');
try {
$pdr = Panier::findOrFail($pn);
$pdr->each(function ($item, $key) {
$user = Commande::create([
'ID_User' => $item->ID_User,
'ID_Piece' => $item->ID_Piece,
'QTE' => $item->QTE,
]);
if ($user && $item->delete()) {
echo 'Commande Confirmée';
}
});
} catch (Illuminate\Database\Eloquent\ModelNotFoundException $e) {
//#Todo handle error
}
}
According to laravel 5.0 documents:
Retrieving A Model By Primary Key Or Throw An Exception
Sometimes you may wish to throw an exception if a model is not found. To do this, you may use the firstOrFail method:
Collection
I am trying to increase the speed of my queries in Laravel 5.7 and I have the call down to ~2.5 seconds. I am trying to figure out more ways to make it faster and if I could get some help I'd greatly appreciate it.
Thanks
How my data is structured:
Function(Controller):
public function getUserDataTmp(Request $request) {
$input = file_get_contents("php://input");
$request = json_decode($input);
if ($this->authTokenAccess($request) == true) {
$bottomWords = bottom_exterior_word::select('word','sentence','sequence','id','group_id')->where('user_id','=', $request->id)->get();
$emergencyWords = left_exterior_word::select('word','sentence','sequence','id')->where('user_id','=', $request->id)->get();
foreach($bottomWords as $tmp => $key) {
$group_id = $key->group_id;
$bottomWords->user_id = $request->id;
$bottomWords[$tmp]->words = $key->getMainWords($group_id, $request->id);
}
foreach($emergencyWords as $key => $word) {
$emergencyWords[$key]->image = imageModel::select('base64','id')->where('emergency_id','=', $word->id)->first();
}
$data = [
'data' => [
'return' => 'success',
'code' => 'VEDC001',
'response' => 'Successfully Gathered Words',
'main_categories' => $bottomWords,
'emergency_words' => $emergencyWords
]
];
return(json_encode($data));
}
}
getMainWords Function(bottom_exterior_word model):
public function getMainWords($group_id, $id)
{
// return("TEST");
$words = \App\main_word::select('id','group_id','sentence','sequence','word')->where('group_id','=', $group_id)->where('user_id','=', $id)->get();
foreach ($words as $key => $word) {
$words[$key]->image = Image::select('base64','id')->where('word_id','=', $word->id)->first();
}
return $words;
}
Start by refactoring so that you dont query inside a foreach loop
foreach($bottomWords as $tmp => $key) {
$group_id = $key->group_id;
$bottomWords->user_id = $request->id;
$bottomWords[$tmp]->words = $key->getMainWords($group_id, $request->id);
}
I would change the getMainWords function to accepts an array of group id's and use the whereIn clause:
The whereIn method verifies that a given column's value is contained
within the given array:
$users = DB::table('users')
->whereIn('id', [1, 2, 3])
->get();
Same treatment for this loop.
foreach($emergencyWords as $key => $word) {
$emergencyWords[$key]->image = imageModel::select('base64','id')->where('emergency_id','=', $word->id)->first();
}
In general minimizing the NUMBER of queries will improve response time.
Old post, would just like to update it though. Since I have first posted this, I have learned a lot more about Laravel and am a lot more experienced with it.
Here is my new function and solution:
Controller:
public function data(Request $request)
{
return response()->success(
[
'emergencywords' => EmergencyWord::with('image')->whereUserId($request->user()->id)->get(),
'categorywords' => CategoryWord::with(['image','words.image'])->whereUserId($request->user()->id)->get(),
]
);
}
Category Word Relationships:
public function image()
{
return $this->hasOne('App\Image','id','image_id');
}
public function words()
{
return $this->hasMany('App\MainWord','category_words_id','sequence');
}
Emergency Word Relationships:
public function image()
{
return $this->hasOne('App\Image','id','image_id');
}
Main Word Relationships:
public function image()
{
return $this->hasOne('App\Image','id','image_id');
}
I have a Form Request called JuridicoFormRequest with the method "rules":
public function rules() {
$rules = [
'rif'=>'required|max:40|unique:juridico,rif',
'correo'=>'required|max:40|unique:juridico,correo',
'd_social'=>'required|max:50',
'r_social'=>'required|max:50',
'pagina_web'=>'max:40|unique:juridico,pagina_web',
'capital'=>'required|numeric',
'fk_lugar'=>'required|integer',
'fk_lugar_fiscal'=>'required|integer',
'fk_tienda'=>'integer',
'num_carnet'=>'max:50'
];
if ($this->juridico){
$rules['rif'] = 'required|max:40';
$rules['correo'] = 'required|max:40';
$rules['pagina_web'] = 'required|max:40';
}
return $rules;
}
The problem is that in the DataBase, "Correo" attribute must be unique, same for "pagina_web". When I try to update some registry "A" and I write the same "Correo" of another registry "B" or the same "pagina_web" of another registry, throws me an error. This does not happen with the "rif" because is the primary key and I already validated it with:
if ($this->juridico) { /*....*/ }
If I do:
if ($this->juridico) {
$rules['rif'] = 'required|max:40';
$rules['correo'] = 'required|max:40|unique:juridico,correo';
$rules['pagina_web'] = 'required|max:40|unique:juridico,pagina_web';
}
Throws me the validation message (not an exception) "correo must be unique" while that "correo" and that "pagina_web" is already taken by the registry that I'm updating.
Is there a way to solve this?
UPDATE: I solved in this way:
public function rules(){
$rules = [
'rif'=>'required|max:40|unique:juridico,rif',
'correo'=>'required|max:40|unique:juridico,correo',
'd_social'=>'required|max:50',
'r_social'=>'required|max:50',
'pagina_web'=>'max:40|unique:juridico,pagina_web',
'capital'=>'required|numeric',
'fk_lugar'=>'required|integer',
'fk_lugar_fiscal'=>'required|integer',
'fk_tienda'=>'integer',
'num_carnet'=>'max:50'
];
if ($this->juridico){
$rules['rif'] = 'required|max:40';
$rules['correo'] = 'required|max:40|unique:juridico,correo,'.$this->juridico.',rif';
$rules['pagina_web'] = 'required|max:40|unique:juridico,pagina_web,'.$this->juridico.',rif';
}
return $rules;
}
use Illuminate\Validation\Rule;
Validator::make($data, [
'email' => [
'required',
Rule::unique('users')->ignore($user->id),
],
]);
If your table uses a primary key column name other than id, you may specify the name of the column when calling the ignore method:
'email' => Rule::unique('users')->ignore($user->id, 'user_id')
you can customize it!
See documentation https://laravel.com/docs/5.5/validation#rule-unique
$rules['correo'] = 'required|max:40|unique:juridico,correo,pagina_web';
or
$rules['correo'] = [
'required',
'max:40',
Rule::unique('juridico')->where(function ($query) use ($paginaWeb) {
return $query->where('pagina_web', $paginaWeb);
})
];
In AppServiceProvider add this
public function boot()
{
Validator::extend('uniq', function ($attribute, $value, $parameters, $validator) {
$data = $validator->getData();
$tableName = $parameters[0];
unset($parameters[0]);
$primaryKey = 'id';
$query = DB::table($tableName);
// set main uniqueness condition
$query->where($attribute, '=', $value);
// if primary key exists - set to NOT be equal (for updating case)
if (!empty($data[$primaryKey])) {
$query->where($primaryKey, '!=', $data[$primaryKey]);
}
// check conditional columns
if (!empty($parameters)) {
foreach ($parameters as $column) {
if (isset($data[$column])) {
$query->where($column, '=', $data[$column]);
}
}
}
$count = $query->count();
return ($count == 0) ? true : false;
});
}
and usage
$rules['correo'] = 'required|max:40|uniq:juridico,correo,pagina_web';