I'm editing a Laravel 4.3 site and I have a db table called categories which has the following fields:
id
parent_id
name
I'm trying to output a list in my view of categories, and their subcategories:
Category
Another Category
Subcat
Subcat
Subcat
I'm not really sure of the best way of achieving this and hoping someone can help point me in the right direction :-)
this is my controller
public function store(Request $request)
{
$this->validate($request, [
'name' =>'required'
]);
$category= new Category;
$category= Category::with('children')->whereNull('parent_id')->get();
$category->name =$request->name;
$category->save();}
I hope this would help you
Method to store the data:
public function store(Request $request) {
$this->validate($request, [
'name' =>'required',
'parent_id' => 'nullable|exists:category,id', //This will check the parent availability
]);
$category= new Category;
if ($request->has('parent_id')) {
$category->parent_id =$request->parent_id;
}
$category->name =$request->name;
$category->save();
return $category;
}
Method to retrieve all data:
public function index() {
$categories = Category::with('children')->all();
return $categories;
}
Method to get category by id :
public function categoryById(Category $category) {
$category = Category::with('children')->where('id', $category->id)->first();
return $category;
}
Related
I am kinda new to both Laravel and Vue and I am working on a school project. I have been following a guide and trying to develop the product but I have the following problem: in the guide was only possible to do an order with a single product. Using LocalStorage a created a Cart component where you can add several products instead. How do I use axios.post to correctly post the order in the database now?
app/Http/Controllers/OrderController.php:
<?php
namespace App\Http\Controllers;
use App\Models\Order;
use Auth;
use Illuminate\Http\Request;
class OrderController extends Controller
{
public function index()
{
return response()->json(Order::with(['product'])->get(),200);
}
public function store(Request $request)
{
$order = Order::create([
'product_id' => $request->product_id,
'user_id' => Auth::id(),
'quantity' => $request->quantity,
'address' => $request->address
]);
return response()->json([
'status' => (bool) $order,
'data' => $order,
'message' => $order ? 'Order Created!' : 'Error Creating Order'
]);
}
public function show(Order $order)
{
return response()->json($order,200);
}
Resources/JS/views/Checkout.vue (between < script > tag):
placeOrder(e) {
e.preventDefault()
let address = this.address
let product_id = this.product.id
let quantity = this.quantity
axios.post('api/orders/', {address, quantity, product_id})
.then(response => this.$router.push('/confirmation'))
},
App/Http/Models/Order.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Order extends Model
{
use SoftDeletes;
protected $fillable = [
'product_id', 'user_id', 'quantity', 'address'
];
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function product()
{
return $this->belongsTo(Product::class, 'product_id');
}
}
Actually, You can achieve your goal by changing many lines of code instead of using your current code at backend (laravel Model-Controller) and frontend (Vue). I will show you how to do by adding hasMany relationship in your User model structure, then changing saving method at controller, and axios request payload. This method has limitation, you have to post an array of products of the same user ID.
Add hasMany relationship in your User Model. Read this
class User extends Model
{
//add this line
public function order()
{
return $this->hasMany(Order::class);
}
Use createMany function to save multiple rows in your controller. Read this
public function store(Request $request)
{
//use this lines to store array of orders
$user = Auth::user();
$orderStored = $user->order()->createMany($request->data);
//return your response after this line
}
Change your axios payload from vue method
data(){
return {
//add new key data to store array of order
arrayOfOrders:[];
};
},
methods:{
placeOrder(e) {
e.preventDefault()
let address = this.address
let product_id = this.product.id
let quantity = this.quantity
//remark these lines, change with storing to arrayOfOrders data instead of doing post request
//axios.post('api/orders/', {address, quantity, product_id})
//.then(response => this.$router.push('/confirmation'))
this.arrayOfOrders.push({
product_id:product_id,
quantity:quantity,
address:address
});
},
//create new function to make post request and call it from your button
postData(){
let instance = this;
axios.post('api/orders/', {
data:instance.arrayOfOrders
}).then(response => this.$router.push('/confirmation'))
}
}
Thank you for your answer! Just one thing is not so clear.. in my OrderController.php should the final code look something like this?
public function store(Request $request)
{
$user = Auth::user();
$order = $user->order()->createMany([
'product_id' => $request->product_id,
'user_id' => Auth::id(),
'quantity' => $request->quantity,
'address' => $request->address
]);
return response()->json([
'status' => (bool) $order,``
'data' => $order,
'message' => $order ? 'Order Created!' : 'Error Creating Order'
]);
}
im making an edit section on my laravel crud project. that when i press edit, it will redirect to the edit.blade.php which will call the slug instead their id. i have worked on it and it shows the error that Too few arguments to function App\Http\Controllers\ProductController::edit(), 1 passed and exactly 2 expected
my Route
Route::get('edit/{slug}', $url. '\productController#update');
Route::get('edit', $url. '\productController#edit');
my ProductController::edit()
public function edit(Product $product, $slug)
{
return view('edit', compact('product'));
}
my ProductController::update()
public function update(Request $request, Product $product, $slug)
{
Product::where('product_slug',$request->$slug)->update([
'product_title' => $request->title,
'product_slug' => $request->slug,
'product_image' => $request->image
]);
// redirect
return redirect('edit');
}
any helps would be greatly appreciated,thanks in advance
You do not need to worry about anything. Just add below code to your Product model:
public function getRouteKeyName()
{
return 'slug';
}
and Laravel will take care of the rest. For more information, visit below link, they have a very good explanation there:
https://laravel.com/docs/8.x/routing#customizing-the-default-key-name
You're getting this error because you have 2 parameters in your methods: Product $product and $slug. To solve this error you should remove one of them.
You also need to add slug parameter to your edit route in order to access it in your controller. And your update route should be PUT or PATCH.
Something like this:
Route::get('products/{slug}/edit', [ProductController::class, 'edit'])
Route::patch('products/{slug}', [ProductController::class, 'update'])
So, you can either remove the Product $product and use the $slug to get the product:
public function edit($slug)
{
$product = Product::where('product_slug', $slug);
return view('edit', compact('product'));
}
public function update(Request $request, $slug)
{
Product::where('product_slug', $slug)->update([
'product_title' => $request->title,
'product_slug' => $request->slug,
'product_image' => $request->image
]);
// redirect
return redirect('edit');
}
Or you can remove the $slug and use the Product $product by setting slug as your route key and changing the route param to product.
// Product model
public function getRouteKeyName()
{
return 'slug'
}
// Product controller
public function edit(Product $product)
{
return view('edit', compact('product'));
}
public function update(Request $request, Product $product)
{
$product->update([
'product_title' => $request->title,
'product_slug' => $request->slug,
'product_image' => $request->image
]);
// redirect
return redirect('edit');
}
// Route
Route::get('products/{product}/edit', [ProductController::class, 'edit'])
Route::patch('products/{product}', [ProductController::class, 'update'])
pass id in your edit button instead of slug
<a href="{{url('edit/'$id'')}}" class="btn btn-info btn-sm" />Edit</a>
and change your update function
public function update(Request $request, Product $product, $id)
{
Product::where('id_column_name', $id)->update([
'product_title' => $request->title,
'product_slug' => $request->slug,
'product_image' => $request->image
]);
// redirect
return redirect('edit');
}
I have 2 models: customer and customerName. In my customer Controller I try to create a method that update fields from both tables. Any idea? Thanks!
CustomerController
public function update(Request $request, Customer $customer)
{
$customer = \App\CustomerName::where('customer_id', $customer->id)->first(); // if I remove this line I can update just "name" from first table
$data = $request->validate([
'name' => 'required|string', //is in customer migration
'first_name'=> 'required', //is in customerName migration
'last_name'=> 'required', //is in customerName migration
]);
$customer->update($data);
return response($customer,200);
}
Customer Model
class Customer extends Model
{
protected $fillable = ['name'];
public function customerName()
{
return $this->hasOne('App\CustomerName');
}
}
CustomerName Model
class CustomerName extends Model
{
protected $fillable = ['first_name', 'last_name'];
public function customer()
{
return $this->belongsTo('App\Customer');
}
}
Assuming customer always has record created for CustomerName, you should then use:
$customer->update(['name' => $data['name']);
$customer->customerName->update(\Arr::only($data, ['first_name', 'last_name']));
and additionally you should wrap this in database transaction like so:
\DB::transaction(function() use ($customer, $data) {
$customer->update(['name' => $data['name']);
$customer->customerName->update(\Arr::only($data, ['first_name', 'last_name']));
});
and of course you should remove this line:
$customer = \App\CustomerName::where('customer_id', $customer->id)->first(); // if I remove this line I can update just "name" from first table
because you should already have $customer object set using Route model binding.
Take a look at your code. You're overriding some variables by naming them the same thing:
public function update(Request $request, Customer $customer)
{
$customer = \App\CustomerName::where('customer_id', $customer->id)->first();
...
Before the line $customer = \App\CustomerName..., $customer is an instance of Customer. After that line, it is an instance of CustomerName, and you no longer have access to the Customer instance. Simply change you naming:
public function update(Request $request, Customer $customer)
{
$customerName = \App\CustomerName::where('customer_id', $customer->id)->first();
// Or, $customerName = $customer->customerName;
// You shouldn't have to query if your relationship is defined properly.
...
Next, save the values accordingly:
$customer->name = $request->input("name"); // or $data["name"]
$customer->save();
$customerName->first_name = $request->input("first_name"); // or $data["first_name"]
$customerName->last_name = $request->input("last_name"); // or $data["last_name"]
$customerName->save();
Set the values of $customer and $customerName accordingly, then call save() on both instances.
You're injecting the Customer instance, so you don't need to load it inside the function. Try this:
public function update(Request $request, Customer $customer)
{
$data = $request->validate([
'name' => 'required|string', //is in customer migration
'first_name'=> 'required', //is in customerName migration
'last_name'=> 'required', //is in customerName migration
]);
$customer->name = $data['name'];
$customer->customerName->first_name = $data['first_name'];
$customer->customerName->last_name = $data['last_name'];
$customer->push(); // This saves the model AND the related models as well.
return response($customer,200);
}
I have a Post/Category manyToMany relations and would like to be able to attach a default category named "Uncategorised" to each new post that is created. How can I do that? A BelongsToMany method only works on the Details page, not on Create page.
BelongsToMany::make(__('Categories'), 'categories', Category::class),
You can also set default value to your database field so that you can omit passing category and will be taken default to Uncategorised like if you are using MySQL you can do it this way by creating migration
$table->text('category')->default(0);
Because the BelongsToMany not show on mode create in Post Nova model. So we have to make our custom Select, by add this code to your fields:
public function fields(Request $request)
{
if($request->editMode=="create"){
$categories = \App\Category::get(['id','name']);
$options = [];
foreach($categories as $value){
$options[$value->id] = $value->name;
}
return [
ID::make()->sortable(),
Text::make('Title'),
Text::make('Summary'),
Textarea::make('Content'),
Select::make('Categories', 'category_id')
->options($options)
->displayUsingLabels()
->withMeta(['value' => 1]) // 1 = id of Uncategorised in categories table
];
}
return [
ID::make()->sortable(),
Text::make('Title'),
Text::make('Summary'),
Textarea::make('Content'),
BelongsToMany::make('Categories','categories')->display('name'),
];
}
Don’t forget relationship function in both, Post and Category model:
class Post extends Model
{
public function categories(){
return $this->belongsToMany(Category::class, 'category_post', 'post_id', 'category_id');
}
}
And:
class Category extends Model
{
public function posts(){
return $this->belongsToMany(Post::class,'category_post', 'category_id', 'post_id');
}
}
Then, custom the function process the data on mode Create of Post resource page, it’s at nova\src\Http\Controllers\ResourceStoreController.php, change function handle to this:
public function handle(CreateResourceRequest $request)
{
$resource = $request->resource();
$resource::authorizeToCreate($request);
$resource::validateForCreation($request);
$model = DB::transaction(function () use ($request, $resource) {
[$model, $callbacks] = $resource::fill(
$request, $resource::newModel()
);
if ($request->viaRelationship()) {
$request->findParentModelOrFail()
->{$request->viaRelationship}()
->save($model);
} else {
$model->save();
// your code to save to pivot category_post here
if(isset($request->category_id)&&($resource=='App\Nova\Post')){
$category_id = $request->category_id;
$post_id = $model->id;
\App\Post::find($post_id)->categories()->attach($category_id);
}
}
ActionEvent::forResourceCreate($request->user(), $model)->save();
collect($callbacks)->each->__invoke();
return $model;
});
return response()->json([
'id' => $model->getKey(),
'resource' => $model->attributesToArray(),
'redirect' => $resource::redirectAfterCreate($request, $request->newResourceWith($model)),
], 201);
}
}
All runs well on my computer. A fun question with me! Hope best to you, and ask me if you need!
What I ended up doing was saving the data on Post Model in boot().
public static function boot()
{
parent::boot();
static::created(function (Post $post) {
$post->categories()->attach([1]);
});
}
Hello i am trying to loop posts that are associated to each category by the slug. It works when i use the ids but when i change my controller function to search by slug it retrieves the slug but does not load the foreach loop.
I have tried so many methods and i don't know where i am going wrong please help.
Category Model :
protected $table = 'post_categories';
public function posts()
{
return $this->hasMany('App\Post', 'id', 'name', 'catslug');
}
Post Model
public function user()
{
return $this->belongsTo('App\User');
}
public function postCategories()
{
return $this->belongsTo('App\PostCategory');
}
Controller
public function getPostCategory($catslug) {
$postCategories = PostCategory::with('posts')
->orderBy('name', 'asc')
->where('catslug', '=', $catslug)
->first();
return view ('articles.category.categoriesposts')->with('postCategories', $postCategories);
}
Route
Route::get('articles/category/{catslug}', [
'uses' => 'ArticlesController#getPostCategory' ,
'as' => 'PostCategory'
] );
View
#foreach($postCategories->posts as $post)
<h4>{{ substr($post->title, 0, 50) }}</h4>
<p>{{ substr($post->body, 0, 90) }}</p>
#endforeach
When i use id there is no problem i cant see what i am doing wrong any feedback will be truly appreciated
Thanks
Ash
Save category id in posts table insted of category name or slug.
Change in post category model:
public function posts()
{
return $this->hasMany('App\Post', 'category_id');
}
Your controller method:
public function getPostCategory($catslug)
{
$postCategories = PostCategory::with('posts')->where('catslug', $catslug)->first();
return view('articles.category.categoriesposts')->with('postCategories', $postCategories);
}
If you want to order posts by name then add orderBy() in relationship :
public function posts()
{
return $this->hasMany('App\Post', 'category_id')->orderBy('name', 'asc');
}