I'm really new to Laravel and ran it some problems. I have a /user/USERNAME view, which obviously, shows the users profile. But now I want to get ALL posts the user posted to be shown on his profile.
My web.php
Route::group(['middleware' => 'auth'], function() {
Route::any('/user/{username}', [
'as' => 'user',
'uses' => 'UserController#view_user'
]);
});
My User model
public function posts() {
return $this->hasMany(Post::class);
}
My Post model
public function user() {
return $this->belongsTo(User::class);
}
My UserController
public function view_user($username = null) {
$user = null;
$user = User::where('name', $username)->firstOrFail();
$posts = Post::where('creator', $username)->get();
return view('/user', [
'user' => $user,
'posts' => $posts
]);
}
public function index() {
$posts = Post::all();
return view('user', compact('posts'));
}
When I'm trying to get the posts with for example {{ $posts->title }} it gives me: Property [title] does not exist on this collection instance.
How am I supposed to do this the correct way?
$posts returns a collection, so in order to retrieve the title of a post you need to loop through the single items of the collection. Then you can use $post->title.
Related
I have a many to many relationship between users and salary_levels. All I have is an error stating Method Illuminate\Database\Eloquent\Collection::salarylevels does not exist. The salarylevels method is defined under User model. How could this happen?
User Model:
public function salarylevels(){
return $this->belongsToMany(SalaryLevel::class)->withPivot(['leave_entitlement']);
}
SalaryLevel:
public function users(){
return $this->belongsToMany(User::class)->withPivot(['leave_entitlement']);
}
Store Method:
public function store(Request $request) {
$users = User::where('id',$request->input('employee_id', []))->get();
$Ids = [
1 => ['leave_entitlement' => '30'],
3 => ['leave_entitlement' => '22']
];
$users->salarylevels()->sync($Ids);
}
Your issue is that $users->salaryLevels() is not correct. $users is a Collection because you have done:
$users = User::where('id', $request->input('employee_id', []))->get();
get() returns a Collection, so you should do:
$users = User::where('id', $request->input('employee_id', []))->first();
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 looked at other similar questions but they are not helping.
My policy method viewAny for User always returns false even if the function body is literally just return true, so I'm not sure what's happening.
On my AuthServiceProvider, I have:
protected $policies = [
'App\Account' => 'App\Policies\AccountPolicy',
'App\User' => 'App\Policies\UserPolicy'
];
UserPolicy:
public function viewAny(User $user, Account $account) {
// $role = $user->roles()->wherePivot('account_id', $account->id)->first();
return true;
}
UserController
protected $account;
// just sharing this so you know where $account comes from
public function __construct()
{
$this->middleware(function($req, $next){
$this->account = Account::find($req->session()->get('account_id'));
return $next($req);
});
}
public function index()
{
$this->authorize('viewAny', $this->account);
$users = $this->account->users;
$usersWithRoles = [];
foreach ($users as $user) {
$usersWithRoles['user'] = $user;
$usersWithRoles['user']['role'] = $this->account->roles()->wherePivot('user_id', $user->id)->first();
}
return view('users.index', [
'users' => $users
]);
}
One thing to note is that my User routes are inside a grouping. So the actual uri looks like /account/users
Route::prefix('account')->group(function() {
/**
* Users
*/
Route::resource('users', 'UserController')->only([
'index', 'destroy', 'update', 'edit'
]);
});
The index route always returns false and a 403 page
The line
$this->authorize('viewAny', $this->account);
will make laravel look for a viewAny function in the AccountPolicy, not in the UserPolicy.
Basically you ask: can user view any account?
Therefore it's an Account policy.
For an admin you might need: can user view any user?
Than its a User policy.
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]);
});
}