Laravel: One to Many Poly relation not updating automatically - laravel

3 types of posts: Personal, Business & Nature.
Below is the Post Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Relation;
Relation::morphMap([
'Personal' => 'App\Personal',
'Business' => 'App\Business',
'Nature' => 'App\Nature',
]);
class Post extends Model
{
public function postable()
{
return $this->morphTo();
}
}
Below is the Personal Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Personal extends Model
{
public function posts()
{
return $this->morphMany(Post::class,'postable');
}
}
Likewise Busines & Nature models.
When I manually enter the data in phpMyAdmin, the tinker shows the result as required, but when I create a post from front-end (via form), the posts table remains unchanged.
I tried googling, but nothing works! :(
Below is the PersonalController
public function create()
{
if(Auth::guest()){
return redirect()->route('login');
}
$sectors = Sector::all();
$cities = City::all();
$ranges = Range::all();
return view('front.personal-register',compact('sectors','cities','ranges'));
}
public function store(Request $request)
{
$this->validate($request,[
"sectors" => "required",
"cities" => "required",
"ranges" => "required",
"g-recaptcha-response" => "required|captcha"
]);
$franchisee = new Personal;
$franchisee->user_id = Auth::user()->id;
$franchisee->save();
$franchisee->sectors()->sync($request->sectors);
$franchisee->cities()->sync($request->cities);
$franchisee->ranges()->sync($request->ranges);
return redirect(route('personal.index'))->with('message','Thank You! Your post has been added');
}

Related

Lavarel & Vue e-commerce: how to post an order as array of products instead of single product

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'
]);
}

Larave 6 - pivot table sync - fire created event only if the attached user is new

I have following code:
$clinic->users()->sync($sync);
Which will go to this class (sync is working):
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class ClinicUser extends Pivot
{
protected $table = 'clinic_user';
static function boot()
{
parent::boot();
static::created(function($item) {
$user = \App\User::find($item->users_id);
$clinic = \App\Models\Clinic::find($item->clinics_id);
if($user->userData->notification_email == 1)
\Mail::to($user->email)->send(new \App\Mail\ClinicManagerAdded(
$user,
$clinic));
if($user->userData->notification_app == 1)
\App\Notification::create([
'title' => "message",
'body' => "message",
'user_id' => $user->id,
]);
});
}
}
Is it possible to fire created method only to the new users (does which weren't detached)?
What i was suggesting is not that robust, infact you need to do
$clinic->users()->detach($sync->pluck('id'));
$clinic->users()->sync($sync);
Every time, and you need to remember it (and so is not robust).
What i feel to suggest you to do is something like this:
Delete the notification in the Model
Create a Service for this operation, let's call it NotyfyUsersNewClinicService (maybe you will find a better name):
<?php
namespace App;
use ...;
class NotyfyUsersNewClinicService{
public __constructor(){}
public updateUsers(Clinic& $clinic, Collection& $newUsers){
$clinic->users->diff($newUsers)->each(function(User $users){
$user->userData->notification_email = true;
\Mail::to($user->email)->send(new \App\Mail\ClinicManagerAdded(
$user,
$clinic));
});
$clinic->users()->sync($sync);
}
}
then you will only need to use this:
(new NotyfyUsersNewClinicService())->updateUsers($clinic, $users);
Note: better if you move the email to a job and send it using queue:work
If someone has a similar problem, I have managed to resolve this by creating the static variable, and fill this variable in the deleted event, like this:
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class ClinicUser extends Pivot
{
protected $table = 'clinic_user';
static $ids = [];
static function boot()
{
parent::boot();
static::deleted(function($item){
self::$ids[] = $item->users_id;
});
static::created(function($item){
if(!\in_array($item->users_id, self::$ids)){
$user = \App\User::find($item->users_id);
$clinic = \App\Models\Clinic::find($item->clinics_id);
if($user->userData->notification_email == 1)
\Mail::to($user->email)->send(new \App\Mail\ClinicManagerAdded(
$user,
$clinic));
if($user->userData->notification_app == 1)
\App\Notification::create([
'title' => "new message",
'body' => "<p>body</p>",
'user_id' => $user->id,
]);
}
});
}
}

How to make Laravel Resources work with polymorphic relation

I have a polymorphic relation in a Laravel application. I want a user of the website to be able to give a rating to both a User model as well as Product model.
I have following models and relations
class Rating extends Model
{
public function ratable()
{
return $this->morphTo();
}
}
class User extends Authenticatable
{
public function ratings()
{
return $this->morphMany('App\Rating', 'ratable');
}
}
class Product extends Model
{
public function ratings()
{
return $this->morphMany('App\Rating', 'ratable');
}
}
and the following database migration:
class CreateRatingsTable extends Migration
{
public function up()
{
Schema::create('ratings', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('ratable_id');
$table->string('ratable_type');
$table->double('rating');
$table->text('comment');
$table->timestamps();
});
}
}
I have defined two routes:
1) Route::post('products/{product}/rating', 'ProductController#setRating')->name('products.rating');
2) Route::post('users/{user}/rating', 'UserController#setRating')->name('users.rating');
I have the following code in the controller (will only show the Product example)
public function setRating(Request $request, Product $product)
{
$rating = new Rating();
$rating->rating = $request->rating;
$rating->comment = $request->comment;
$product->ratings()->save($rating);
}
The above works perfectly and the correct records get inserted in the database depending on whether the Product route or the User route is called.
Now, all the rest of my code is using Laravel Resources, and for consistency reasons, I have also defined a resource for Rating:
class RatingResource extends JsonResource
{
public function toArray($request)
{
return [
'ratable_id' => $this->ratable_id,
'ratable_type' => $this->ratable_type,
'rating' => $this->rating,
'comment' => $this->comment
];
}
}
I'm also changing the ProductController code to use this resource
public function setRating(Request $request, Product $product)
{
return new RatingResource(Rating::create([
'ratable_id' => $product->id,
'ratable_type' => $product,
'rating' => $request->rating,
'comment' => $request->comment,
]));
}
In postman, I'm calling the REST API:
http://{{url}}/api/products/1/rating with body:
rating: 4
comment: "Test"
Yet, I always get following error message
"SQLSTATE[HY000]: General error: 1364 Field 'ratable_id'
doesn't have a default value (SQL: insert into ratings (rating,
comment, updated_at, created_at) values (4, test, 2019-09-07
13:44:22, 2019-09-07 13:44:22))"
I'm not passing the ratable_id and ratable_typeas I'm filling these in already in the controller code.
I somehow need to pass the resource that it's a Productor a UserI'm giving a rating for.
How can I make this work?
The problem probably is that ratable_id is missing from $fillable.
Try $product->ratings()->create([...data...]) so you don't have to set ratable_id and ratable_type yourself:
public function setRating(Request $request, Product $product)
{
return new RatingResource(
$product->ratings()->create([
'rating' => $request->rating,
'comment' => $request->comment,
])
);
}

How to Create Model with Notifiable Trait

I want create a Model with Notifiable feature,
First,in my controller :
$collection = collect([
[
'name' => 'user1',
'email' => 'user1#gmail.com',
],
[
'name' => 'user2',
'email' => 'user2#gmail.com',
],
[
'name' => 'user1000',
'email' => 'user1000#gmail.com',
],
]);
$u3 = new User3($collection);
when I return $u3->getEmailList(); , output is :
[{"name":"user1","email":"user1#gmail.com"},{"name":"user2","email":"user2#gmail.com"},{"name":"user1000","email":"user1000#gmail.com"}]
my class for User3 is:
namespace App;
use App\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Notification;
use Illuminate\Notifications\RoutesNotifications;
use Notifications\EmailClientOfAccount;
class User3 extends User
{
use Notifiable;
public $emailList;
public function __construct($emails)
{
$this->emailList = $emails;
}
public function getEmailList()
{
return $this->emailList;
}
public function routeNotificationForMail($notification)
{
return $this->emailList['email'];
}
}
Then, I pass $u3 to Notification as:
Notification::send($u3->getEmailList(), new
SendMailNotification($template,$subject,$request->input('mailFromTitle'),$attachments));
It show below error:
Symfony\Component\Debug\Exception\FatalThrowableError: Call to a member function routeNotificationFor() on array
can you help me for solve this problem,Please?
Thanks in Advance,
//-------------------
I correct to :
Notification::send($u3, new SendMailNotification($template,$subject,$request->input('mailFromTitle'),$attachments));
In my My Notification:
public function toMail($notifiable)
{
return new EmailTo($notifiable,$this->view,$this->topic,$this-
>mailFrom,$this->attaches);
}
and in Build():
public function build()
{
$email= $this->view($this->view);
return $email;
}
But it not work, I dont know where is mistake?
Notification send expects a Notifiable object, not the email list itself, if you change it to this, you should get further.
Notification::send($u3, new SendMailNotification($template,$subject,$request->input('mailFromTitle'),$attachments));

Laravel: How to use the same post form for multiple sections

I want to build something like facebook and I have a form where the user can post content but I want to use the same form on different sections, for example: groups, pages, profile.
I have PostController that is a resource that receives the post requests but I need a way to differentiate between sections in order to store the data with the correct section_type and section_id.
// Post Model Post.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = ['content', 'user_id'];
public function section()
{
return $this->morphTo();
}
public function user()
{
return $this->belongsTo(User::class);
}
public function group()
{
return $this->belongsTo(Group::class);
}
public function page()
{
return $this->belongsTo(Page::class);
}
}
The User.php Model
class User extends Authenticatable
{
public function posts()
{
return $this->morphMany(Post::class, 'section');
}
}
and this is the PostController and store() function where I have only one situation at this moment where a post is stored in section_type App\User, but I need a way to store it in App\Group or App\Page also.
public function store(Request $request)
{
$this->validate(request(),
[
'content' => 'required|min:5',
]);
$user = User::find(Auth::id());
$user->posts()->create([
'content' => $request->content,
'user_id' => Auth::id()
]);
return redirect('/');
}
What do I need to do in PostController.php?
I thought maybe I could use a post request with parameters in the form, like this for posting in groups
<form method="post" action="/?group=1">
or this for posts in pages
<form method="post" action="/?page=1">
And after that use $request->query() to get the section and id. Do you have other ideas?

Resources