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.
Related
So I have 2 tables: Item and Product. An Item hasMany Products and a Product belongsTo an Item.
Products migration:
Schema::create('products', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('hashed_id')->nullable()->unique();
$table->bigInteger('item_id')->unsigned();
$table->bigInteger('user_id')->unsigned();
$table->integer('state')->default(1);
$table->decimal('price');
$table->string('slug')->nullable()->unique();
$table->timestamps();
$table->foreign('item_id')->references('id')->on('items')->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
For the hashed_id I use the following package: https://packagist.org/packages/hashids/hashids to create a hashed id to show in the url.
Product.php
public static function boot()
{
parent::boot();
static::created(function ($product) {
$productId = $product->id;
$hashids = new Hashids("", 10, 'abcdefghijklmnopqrstuvwxyz1234567890');
$hashedId = $hashids->encode($productId++);
$slug = Str::slug($product->item->slug . '-' . $hashedId);
$product->hashed_id = $hashedId;
$product->slug = $slug;
});
}
ProductsController.php
public function createSelfProduct(Request $request)
{
$product = auth()->user()->products()->create([
'item_id' => $request->item_id,
'user_id' => auth()->user()->id,
'price' => $request->price,
]);
// create the product and show seller info
return new ProductResource($product->load('user'));
}
What I'm trying to do is that when a user creates a new product, it should get the slug from the item model, put the $hashedId behind it and save that to the db. Now, when I do a post request via Postman, I get the desired result, as hashed_id and slug are saved. But when I check the database, both hashed_id and slug are NULL. Only the item_id, user_id and price are saved. What am I doing wrong and how can I fix this?
The created event means the Model has already been created. This is not before save, but after it has been saved. If you alter anything at this point you will need to save the record again.
Simply: You forgot to call save on your model instance to save the changes after you altered it.
Laravel has a convenient way of handling this with Observers
https://laravel.com/docs/5.8/eloquent#observers
php artisan make:observer ProductObserver
Then in Observers/ProductObserver.php
public function created(Product $product) {
$product = ''; // whatver you need to do here. $product is an instance of Product model
// Dont forget to save your model after modifying
$product->save();
}
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 using dimsav for multilanguage and I have this problem after doing step by step from the guid. (dimsav)
I have a Model Category:
use Illuminate\Database\Eloquent\Model;
use Dimsav\Translatable\Translatable;
class Category extends Model {
use Translatable;
public $translatedAttributes = ['name'];
}
A CategoryTranslation:
use Illuminate\Database\Eloquent\Model;
class CategoryTranslation extends Model {
public $timestamps = false;
}
And in Controller when I try to save this with a specific language I get an error. This is my controller:
$language = App::getLocale();
$user = Auth::user();
$category = new Category();
$category->translate('en')->name = Input::get('name'); //line 35
$category->save())
And error:
at HandleExceptions->handleError('2', 'Creating default object from
empty value',
'C:\workspace\applications\wamp\www\lutz-paletten\app\Http\Controllers\CategoryController.php',
'35', array('language' => 'en', 'user' => object(User), 'category' =>
object(Category))) in CategoryController.php line 35
PS: this is my migration:
Schema::create('categories', function (Blueprint $table) {
$table->increments('id');
$table->integer('categoryId');
$table->integer('user_id');
$table->timestamps();
});
Schema::create('category_translations', function (Blueprint $table) {
$table->increments('id');
$table->integer('category_id')->unsigned();
$table->string('name');
$table->string('locale')->index();
$table->unique(['category_id','locale']);
$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
});
What am I missing ?
If I use this, it works:
$category->name = Input::get('name');
And it will be saved with what is set as AppLocale but how can I use it with translate() ?
I don't know if you solved this or not, but i think you should check with a couple of things:
1- delete the Parentheses when initiating the Category object so it will become:
$category = new Category;
2- Change the extra Parentheses after the save function so it will be:
$category->save();
3- make sure your input is named correctly.
and that's all i can see, hope you solved already :).
BTW you don't need that
$table->integer('categoryId'); in your migration is not nessary since $table->increments('id); is playing that role!
happy coding :)
If you create a new record of Category, this last one saves record with your current Locale (default : en).
You just need to change $category->translate('en') by $category->getNewTranslation('en') or $category->translateOrNew('en') and it works !
For your example:
Create a category with default locale (config/app.php ==> locale => 'en'):
// CategoryController
public function createCategory(Request $request)
{
// Save record in *categories* table
// And save the default language (config/app.php ==> locale) in *category_translations* table.
$category = new Category::create($request);
}
Create a translation in an existing category:
public function createCategoryTranslation(Request $request, $id)
{
$category = Category::find($id)
// Solution 1 : If you want to explain the fields to be saved.
$category->getNewTranslation('en')->name = $request->input('name');
// Solution 2 : Mass assignement if you have multiple fields to be saved.
$category->getNewTranslation('en')->fill($request);
$category->save()
}
Update a translation:
public function updateCategoryTranslation(Request $request, $id)
{
$category = Category::find($id)
// Solution 1 : If you want to explain the fields to be saved.
$category->translate('en')->name = $request->input('name');
// Solution 2 : Mass assignement if you have multiple fields to be saved.
$category->translate('en')->fill($request);
$category->save()
}
CreateOrUpdate translation:
public function createOrUpdateCategoryTranslation(Request $request, $id)
{
$category = Category::find($id)
// Solution 1 : If you want to explain the fields to be saved.
$category->translateOrNew('en')->name = $request->input('name');
// Solution 2 : Mass assignement if you have multiple fields to be saved.
$category->translateOrNew('en')->fill($request);
$category->save()
}
I have problem with saving data to m:n table layout in laravel 5. I have table appliances and table documentations, where pivot table is documentation_appliance.
Models are:
class Appliances extends Model
{
public function documentations()
{
return $this->belongsToMany('documentations');
}
}
and
class Documentation extends Model
{
public function appliances()
{
return $this->belongsToMany('appliances');
}
}
Now I try to save data to table in my Controller
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required|max:255',
'file_name' => 'required',
]);
if($request->hasFile('file_name') ) {
$fname = $request->file('file_name')->getClientOriginalName();
$request->file('file_name')->move(
base_path() . '/public/files/documentation/', $fname
);
}
$document = new Documentation();
$document->name = $request->name;
$document->filename = $fname;
if($document->save()) {
$doc_ids = $request->documentation_appliance;
$document->appliances()->sync($doc_ids);
}
return view('backend.documentation.index', [
'documentations' => $this->documents->getDocuments(),
]);
}
Data to table documents are saved corectly, image is stored, but I have problem with saving data to pivot table. Screen displays me this error:
FatalErrorException in compiled.php line 10191:
Class 'appliances' not found
in compiled.php line 10191
nothing more, I guess I have bad use of class somewhere or am I doing bad something else? Thanks everyone for help.
according to https://laravel.com/docs/5.2/eloquent-relationships#many-to-many your table name must be appliance_documentation not documentation_appliance.
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();
}