I have several tables (all of them with created_at, updated_at, deleted_at) :
sectors
lang_sector
valuechains
lang_valuechain
segments
lang_segment
keyneeds
keyneed_lang
the tables are linked in this order :
sectors has many valuechains
valuechains has many segments
segments has many keyneeds
Here is my model :
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Sector extends Model
{
use SoftDeletes;
protected $table = "sectors";
protected $fillable = ['admin_id'];
protected $dates = [ 'created_at', 'updated_at', 'deleted_at' ];
public function langs() {
return $this->belongsToMany('App\Lang')
->withPivot('sectname', 'sectshortname', 'segname_slug',
'sectdescription', 'sectshortdescription'
)
->withTimestamps();
}
public function admin()
{
return $this->belongsTo('App\Admin');
}
public function valuechains()
{
return $this->hasMany('App\Valuechain');
}
public function segments()
{
return $this->hasManyThrough('App\Segment', 'App\Valuechain');
}
public function keyneeds()
{
return $this->hasManyThrough('App\Keyneed', 'App\Segment', 'App\Valuechain');
}
}
In my destroy controller :
public function destroy($id)
{
$sector = Sector::findOrFail($id);
$sector_ids = $sector->langs()->allRelatedIds();
foreach ($sector_ids as $id){
$sector->langs()->updateExistingPivot($id, ['lang_sector.deleted_at' => Carbon::now()]);
}
$sector->valuechains()->update( [ 'valuechains.deleted_at' => Carbon::now() ] );
$sector->segments()->update( [ 'segments.deleted_at' => Carbon::now() ] );
$sector->keyneeds()->update( [ 'keyneeds.deleted_at' => Carbon::now() ] );
Sector::where('id', $id)->delete();
return redirect()->route('sectors.index')->with('success', 'Sector deleted');
}
My issue is that it doesn't update the following tables : segments and keyneeds (which have created_at, updated_at and deleted_at fields) and their pivot tables too ... And that i have an error message :
SQLSTATE[23000]: Integrity constraint violation: 1052 Field: 'updated_at' in field list is ambiguous (SQL: update segments inner join valuechains on valuechains.id = segments.valuechain_id set segments.deleted_at = 2018-05-10 06:54:54, updated_at = 2018-05-10 06:54:54 where valuechains.sector_id = 2)
it adds : updated_at
I succesfully updated :
sectors table
valuechains table
lang_valuechain pivot
My issue is to update
segments table by using : $sector->segments()
keyneeds table by using : $sector->keyneeds()
And their pivot table as well... I read the documentation but it doesn't help.
I use the update() method because $sector->segments()->delete() is trying to make a hard delete...
I finally find an issue to this.. Not very elegant but it's working on the "main" tables. I'll have to solve the little problem on pivot tables :
public function destroy($id)
{
$sector = Sector::findOrFail($id);
$valuechains = Valuechain::where('sector_id','=',$sector->id)->get();
foreach ($valuechains as $valuechain) {
$segments = Segment::where('valuechain_id', '=', $valuechain->id )->get();
$valuechain->langs()->updateExistingPivot($valuechain->id, ['lang_valuechain.deleted_at' => Carbon::now()]);
foreach ($segments as $segment) {
$keyneeds = Keyneed::where('segment_id', '=', $segment->id)->get();
$segment->langs()->updateExistingPivot($segment->id, ['lang_segment.deleted_at' => Carbon::now()]);
$segment->delete();
foreach ($keyneeds as $keyneed) {
$keyneed->langs()->updateExistingPivot($keyneed->id, ['keyneed_lang.deleted_at' => Carbon::now()]);
$keyneed->delete();
}
}
$valuechain->delete();
}
$sector->langs()->updateExistingPivot($id, ['lang_sector.deleted_at' => Carbon::now()]);
$sector->delete();
return redirect()->route('sectors.index')->with('success', 'Secteur suppprimé');
}
Related
I have liste.blade.phpa liste that contains all the candidats and their informations. For each candidat I need to display all the data of only his last candidature
Question :
How can I show all the columns of only the last candidature of each candidat ?
Candidat Model :
protected $fillable = [
'service_id',
'demande',
'nom',
'telephone',
'cin',...
];
public function candidatures()
{
return $this->hasMany(Candidature::class);
}
Candidature Model
protected $fillable = [
'candidat_id',
'doc',
'date_depot',
'etat',
'demande',
];
public function candidat()
{
return $this->belongsTo(Candidat::class, 'candidat_id');
}
Candidat controller :
public function index()
{
$candidat = Candidat::join('candidatures', 'candidatures.candidat_id', '=', 'candidats.id')
->whereNotIn('etat', ['Archivé', 'Refusé'])
->where('candidats.deleted','false')
->select('candidats.*)
->get();
return view('candidat.liste', compact('candidat'));
}
liste.blade.php:
#foreach($candidat as $key => $data)
<tr>
<th>$data->id</th>
...
#foreach($data->candidatures as $candidature)
<td>{{$candidature->demande }}</td>
<td>{{$candidature->date_depot}}</td>
<td>{{$candidature->etat}}</td>
#endforeach
#endforeach
Ps: I have to submit my project today
Thank you for your help .
Here You can use Order By clause and sort in dese to get last record
$candidat = Candidat::join('candidatures', 'candidatures.candidat_id', '=', 'candidats.id')
->whereNotIn('etat', ['Archivé', 'Refusé'])
->where('candidats.deleted','false')
->select('candidats.*)
->orderBy('candidatures.id','desc')
->limit(1)
->get();
Since you have relationships defined you can use
Candidat::with('candidatures') instead of Candidat::join('candidatures', 'candidatures.candidat_id', '=', 'candidats.id') or even ->candidatures like
$candidats = Candidat::all();
And in foreach ($candidats as $candidat) run $candidat->candidatures()->latest()->first()
I'm working on laravel e-commerce project where I need to add Attributes to my posts (image below as example)
My question is how to achieve that? should i create new tables or can I add manually from post.create like any other e-commerce cms?
Personally I prefer to be able to add fields in post.create like I
add + button and each time I click on it 2 input fields add and I
can put key and value in it. (if you can help me with that)
Thanks.
Update:
With suggest of #anas-red I've created this structure now:
attributes table.
Schema::create('attributes', function (Blueprint $table) {
$table->increments('id');
$table->string('title')->unique();
$table->timestamps();
});
and product_attributes table
Schema::create('product_attributes', function (Blueprint $table) {
$table->increments('id');
$table->integer('product_id')->unsigned();
$table->foreign('product_id')->references('id')->on('products');
$table->integer('attribute_id')->unsigned();
$table->foreign('attribute_id')->references('id')->on('attributes');
$table->string('attribute_value')->nullable();
$table->timestamps();
});
now i have this store method on my controller when i save my posts:
public function store(Request $request)
{
//Validating title and body field
$this->validate($request, array(
'title'=>'required|max:225',
'slug' =>'required|max:255',
'user_id' =>'required|numeric',
'image_one' =>'nullable|image',
'image_two' =>'nullable|image',
'image_three' =>'nullable|image',
'image_four' =>'nullable|image',
'image_one' =>'nullable|image',
'short_description' => 'nullable|max:1000',
'description' => 'nullable|max:100000',
'subcategory_id' => 'required|numeric',
'discount' => 'nullable|numeric',
'discount_date' => 'nullable|date',
'price' => 'required|numeric',
));
$product = new Product;
$product->title = $request->input('title');
$product->slug = $request->input('slug');
$product->user_id = $request->input('user_id');
$product->description = $request->input('description');
$product->short_description = $request->input('short_description');
$product->subcategory_id = $request->input('subcategory_id');
$product->discount = $request->input('discount');
$product->discount_date = $request->input('discount_date');
$product->price = $request->input('price');
if ($request->hasFile('image')) {
$image = $request->file('image');
$filename = 'product' . '-' . time() . '.' . $image->getClientOriginalExtension();
$location = public_path('images/');
$request->file('image')->move($location, $filename);
$product->image = $filename;
}
$product->save();
$product->attributes()->sync($request->attributes, false);
//Display a successful message upon save
Session::flash('flash_message', 'Product, '. $product->title.' created');
return redirect()->route('admin.products.index');
}
The process i want to do is this:
Store my attributes
Select my attributes while creating new post
Give value to selected attribute
save post_id arribute_id and atteribute_value in product_attributes table.
here is the error i get:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'attributes_id'
in 'field list' (SQL: select attributes_id from product_attributes
where product_id = 29)
UPDATE:
Product model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use jpmurray\LaravelCountdown\Traits\CalculateTimeDiff;
class Product extends Model
{
use CalculateTimeDiff;
protected $table = 'products';
protected $fillable = [
'title', 'slug', 'image_one', 'image_two', 'image_three', 'image_four', 'short_description', 'description', 'price', 'discount', 'discount_date',
];
public function category(){
return $this->belongsTo(Category::class);
}
public function subcategory(){
return $this->belongsTo(Subcategory::class);
}
public function attributes()
{
return $this->belongsToMany(Attribute::class, 'product_attributes', 'product_id', 'attribute_id');
}
public function order(){
return $this->hasMany(Order::class);
}
public function discounts(){
return $this->hasMany(Discount::class, 'product_id', 'id');
}
}
Attribute model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Attribute extends Model
{
protected $fillable = [
'title',
];
public function products(){
return $this->belongsToMany(Product::class);
}
}
I think you can add new table lets say "post_attributes" with the following columns:
id - post_id - key - value
in the PostAttribute model add this:
public function post
{
return $this->belongsTo(Post::class);
}
in the Post model add the following:
public function attributes
{
return $this->hasMany(PostAttributes::class, 'post_attributes');
}
Now the app is flexible enough to handle multiple attributes to one post or a single attribute to another.
Other approach is to implement JSON in your database. Hope that helped you.
update Product model
public function attributes()
{
return $this->belongsToMany(Attribute::class, 'product_attributes', 'product_id', 'attribute_id')->withPivot('attribute_value')->withTimestamps();
}
and update Attribute model to
public function products()
{
return $this->belongsToMany(Product::class, 'product_attributes', 'attribute_id', 'product_id')->withPivot('attribute_value')->withTimestamps();
}
If I see your Product and Attribute Models I will be in a better position to answer you properly.
But any way, I think your problem is with the product_attributes table.
This table is now acting as a pivot (intermediate) table and it is not following Laravel naming convention. The convention is to name it as follows: attribute_product.
Next, you have to add the following into both models i.e. Product and Attribute.
in Attribute Model add:
$this->belongsToMany(Product::class)->withPivot('value');
in Product Model add:
$this->belongsToMany(Attribute::class)->withPivot('value');
To add value to more_value column on pivot table. Use the following:
$product->attributes()->attach($attributeId, ['more_value' => $string]);
or use sync:
$product->attributes()->sync([$attributeId => ['more_value' => $string]]);
lol. the important part is repo code is:
<input type="hidden" id="appOrderItems" name="orderItems[]">
trace appOrderItems in my JS section and you will get it.
in simple words:
when the user adds attributes to a product (in my case, items to an order) then, the appOrderItems array will get the id of the attribute and any additional value that you need to add into the pivot table (other than the product_id and attribute_id. in your case the mores_value). After gathering these attributes into appOrderItems JS array I push its value to the hidden HTML field (name="orderItems[]"). in this case it will be sent to the controller for further process.
I have user input following the rules below;
public function rules()
{
return [
'phone_number' => 'required|array',
'amount' => 'required|string|max:4',
'phone_number_debit' => 'required|string|max:15',
];
}
I would want to save the data in a model Transaction. For the phone_number it is an array that could have one value or multiple. So that leaves for foreach loop.
This is what I want to achieve, save different rows determined by the number of records in the array.
$transaction = new Trasaction();
$transaction->phone_number = $req->phone_number; //Value in the array
$transaction->amount = $req->amount;
$transaction->phone_number_debit = $req->phone_number_debit;
$transaction->save();
Save diffrent records according to the records in the phone_number array.
However I can not think of a way to achieve this.
Anyone?
try this :
$data = request(['amount', 'phone_number', 'phone_number_debit']);
foreach($data['phone_number'] as $phone_number) {
Trasaction::create([
'amount' => $data['amout'],
'phone_number' => $phone_number,
'phone_number_debit' => $data['phone_number_debit']
]);
}
make sure in your Trasaction modal you've set to fillable property like this :
class Trasaction extends Model
{
protected $fillable = ['amount', 'phone_number', 'phone_number_debit'];
}
There are many ways to do this, in a nutshell:
collect(request('phone_number'))->each(function ($phone) use ($req) {
$transaction = new Trasaction();
$transaction->phone_number = $phone; // element of the array
$transaction->amount = $req->amount;
$transaction->phone_number_debit = $req->phone_number_debit;
$transaction->save();
});
TL;DR
One-to-Many Relationship
In order to get a better code, you can create a transaction_phones table, creating a one-to-many relationship.
You'll create a TransactionPhone model and add this:
public function transaction()
{
return $this->belongsTo(Transaction::class);
}
The TransactionPhone migration:
Schema::create('transaction_phones', function (Blueprint $table) {
$table->increments('id');
$table->integer('transaction_id');
$table->string('phone_number');
$table->timestamps();
});
In your Transaction model you'll have the inverse:
public function phones()
{
return $this->hasMany(TransactionPhone::class);
}
public function addPhone($phone)
{
return $this->phones()->create(['phone_number' => $phone]);
}
And in you Controller:
$transaction = Trasaction::create(request()->only('amount', 'phone_number_debit'));
collect(request('phone_number'))->each(function ($phone) use ($transaction) {
$transaction->addPhone($phone);
});
I hope this answer can help you.
I am totally new on Laravel, and I have implement the User table provided by Laravel Auth, and also I have create a table for the user meta data that is a Key Value pare table.
The user meta table is created by the following code :
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class UserMeta extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('user_meta', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->char('meta_key', 255);
$table->longText('meta_value')->nullable();
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onUpdate('cascade')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('user_meta');
}
}
In my User model I have the following method:
public function meta() {
return $this->hasMany('App\Models\UserMeta');
}
and inside my UserMeta model I have the following method:
public function user() {
return $this->belongsTo('App\User');
}
Until now anything is fine. So, when I register a new user I perform the following actions:
$user = User::create(
[
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt( $data['password'] ),
]
);
if ( $user ) {
$telephone_number = new UserMeta;
$telephone_number->user()->associate($user);
$telephone_number->meta_key = 'telephone_number';
$telephone_number->meta_value = $data['telephone_number'];
$telephone_number->save();
$company = new UserMeta;
$company->user()->associate($user);
$company->meta_key = 'company';
$company->meta_value = $data['company'];
$company->save();
$web_site = new UserMeta;
$web_site->user()->associate($user);
$web_site->meta_key = 'web_site';
$web_site->meta_value = $data['web_site'];
$web_site->save();
}
return $user;
I suppose that should be a better way to perform that same actions, but I don't know what is the other way :( :)
So, the above code works very nice for me, but now the problem is with the value update. In this case, how can I update the Meta Data when I update the user profile ?
In my update method of my UserControler, I perform the following actions:
$user = User::where( 'id', '=', $id )->first();
$user->name = $request->input( 'name' );
$user->email = $request->input( 'email' );
$user->password = bcrypt( $request->input( 'password' ) );
$user->save();
My $request->input(); has the following extra fields that corresponding to meta values telephone_number, web_site, company.
So, how can I update the meta values in the user_meta table ?
Looping through values
Firstly, you are right that you could loop through the three keys in your create method to:
// Loop through all the meta keys we're looking for
foreach(['telephone_number', 'web_site', 'company'] as $metaKey) {
$meta = new UserMeta;
$meta->meta_key = $metaKey;
$meta->meta_value = array_get($data, $metaKey);
$meta->save();
}
The Update Method: Approach One
Then, in your update method
// Loop through all the meta keys we're looking for
foreach(['telephone_number', 'web_site', 'company'] as $metaKey) {
// Query for the meta model for the user and key
$meta = $user->meta()->where('meta_key', $metaKey)->firstOrFail();
$meta->meta_value = array_get($data, $metaKey);
$meta->save();
}
Note the firstOrFail() to end the query. This is just me being strict. If you wanted to add a meta value if it didn't exist, then you could replace that line with
// Query for the meta model for the user and key, or
// create a new one with that key
$meta = $user->meta()->where('meta_key', $metaKey)
->first() ?: new UserMeta(['meta_key' => $metaKey]);
The Update Method: Approach Two
This approach is a little more efficient, but a more complex (but also potentially teaches about a cool feature of Eloquent!).
You could load in all of the meta keys first (see lazy eager loading).
// load the meta relationship
$user->load('meta');
// Loop through all the meta keys we're looking for
foreach(['telephone_number', 'web_site', 'company'] as $metaKey) {
// Get the first item with a matching key from the loaded relationship
// Or, create a new meta for this key
$meta = $user->meta
->first(function($item) use ($metaKey) {
return $item->meta_key === $metaKey;
}) ?: new UserMeta(['meta_key' => $metaKey]);
$meta->meta_value = array_get($data, $metaKey);
$meta->save();
}
I have the following model.
class Training extends \Eloquent {
// Add your validation rules here
public static $rules = [
'name' => 'required',
'city' => 'required',
'province' => 'required',
'budget_year' => 'required|integer',
's_date' => 'required|date',
'e_date' => 'required|date'
];
// Don't forget to fill this array
protected $fillable = [
'name',
'city',
'province',
'budget_year',
's_date',
'e_date'
];
public function material(){
return $this->hasMany('Material');
}
public function budget(){
return $this->belongsToMany('Budget')->withPivot('amount');
}
public function budgetById($training_id){
$this->belongsToMany('Budget')->where('training_id', '=', $training_id)->get();
}
}
when I debug the budgetById method using DB::getQueryLog, the query is as follow
select budgets.*,
budget_training.training_id as pivot_training_id,
budget_training.budget_id as pivot_budget_id
from budgets inner join budget_training on budgets.id = budget_training.budget_id
where budget_training.training_id is null and training_id='6'
which return 0 rows, but when I try to modify the query and run it in pgadmin, the following script works well.
select budgets.*,
budget_training.training_id as pivot_training_id,
budget_training.budget_id as pivot_budget_id
from budgets inner join budget_training on budgets.id = budget_training.budget_id
where budget_training.training_id='6'
notice I remove training_id is null and from Laravel generated query. What is wrong with my budgetById method?
You have called get() and didn't use return here:
public function budgetById($training_id){
// = in where is optional in this case
$this->belongsToMany('Budget')->where('training_id', '=', $training_id);
}
You should use like this:
public function budgetById($training_id){
// = in where is optional in this case
return $this->belongsToMany('Budget')->where('training_id', '=', $training_id);
}
In Lavarel 7.X, you can use the wherePivot method to filter columns on the pivot table, like this:
return $this->belongsToMany('Budget')->wherePivot('training_id', '=', $training_id);
or
return $this->belongsToMany('Budget')->wherePivotNotNull('training_id');