What is better way to pass parameters into service method? - laravel

Here is a service that implements interface
interface Rating
{
function create(User $user, User $rateduser, Rating $rating, Order $order);
}
The controller acceepts the request object:
{"rating": [{"rateid": 1, "rate": 4}], "userid": 1}
When I try to pass whole data into service:
public function store(RateRequest $request)
{
$rating = RatingData::from(
[
'rateid' => $request->get('rating.rateid'),
'rate' => $request->get('rating.rate'),
]
);
$user = Auth()::user();
$rateduser = User::find($request->id);
$order = new Order;
$this->rating->create($user, $rateduser, $rating, $order);
}
How is better pass parametres into funciton using ideniticators $userid, $rateUserId, $orderId or concrete models? Or left it such as now?
interface Rating
{
function create(int $userid, int $rateUserId, Rating $rating, int $order);
}
The controller looks dirty in my case.
Another problem what if tomorrow my model will be changed from eloquent to another? Then I have to change service methods

I can suggest the following approach if you want to keep your controllers clean. (I do not claim that it is the only correct one)
Dto:
class RatingDto extends DataTransferObject
{
public int $rateId;
public int $rate;
}
class CreateRatingDto extends DataTransferObject
{
public int $userId;
public int $rateUserId;
/** #var RatingDto[] */
#[CastWith(ArrayCaster::class, itemType: RatingDto::class)]
public array $ratings;
}
Request:
class CreateRatingRequest extends FormRequest
{
public function rules(): array
{
return [
'ratedUserId' => 'required|integer|exists:users,id',
'ratings' => 'required|array',
'ratings.*.rateId' => 'required|integer',
'ratings.*.rate' => 'required|integer|min:1|max:5',
];
}
public function getDto(): CreateRatingDto
{
return new CreateRatingDto([
'userId' => Auth::user()->id,
'ratedUserId' => $this->input('ratedUserId'),
'ratings' => $this->input('ratings'),
]);
}
}
Interface:
interface RatingServiceInterfate
{
public function create(CreateRatingDto $dto): RatingModel;
}
Controller:
class RatingController extends Controller
{
private RatingServiceInterfate $ratingService;
public function __construct(RatingServiceInterfate $ratingService)
{
$this->ratingService = $ratingService;
}
public function store(CreateRatingRequest $request): JsonResource
{
$rating = $this->ratingService->create($request->getDto());
return new RatingResource($rating);
}
}
Also, this implementation does not bind your service to models. At any time you can write a new interface implementation and substitute it through ServiceProvider. (if for example in another part of the application you need to save data to another database not compatible with eloquent)

Related

laravel relation data to search for other models

Client model has relations to Invoice. I need to get the amounts from the Invoice relationship and find the matching transactions from the Transaction model.
I do it like this:
class Client extends Model
{
public function invoices()
{
return $this->hasMany(Invoice::class);
}
public function priceInvoices()
{
return $this->hasMany(Invoice::class)->select('gross_price');
}
}
foreach (Client::find($id)->priceInvoices->toArray() as $item) {
$prices[] = $item['gross_price'];
}
$transactions_for_prices = Transaction::whereIn('price', $prices)->get();
Will it be possible to make it more elegant?
If your Invoice.php Model has a relationship with a Transaction.php Model,
Example:
class Invoice extends Model
{
public function trasnactions()
{
return $this->hasMany(Transaction::class);
}
}
You could do something like this in a controller. (example)
public function show($id) {
$client = Client::findOrFail($id);
return view('view_name', [
'client' => $client,
'transactions => $client->invoices->transactions
]);
}

Creating a new record using a One to Many relation in Laravel

I have a sample relation between two models, User and Announcement as displayed below.
class Announcement extends Model
{
//
protected $guarded = [];
public function user(){
return $this->belongsTo(User::class);
}
}
class User extends Model
{
//
protected $guarded = [];
public function announcements(){
return $this->hasMany(Announcement::class);
}
}
Currently I am trying to create a new announcement, using the relation but it throws an error
"message": "Call to a member function announcements() on null",
This is the current state of my api from the controller
class AnnouncementController extends Controller
{
public function store(Request $request)
{
//
$record = request()->user()->announcements()->create($this->validateRequest());
return (new AnnouncementResoure($record))
->response()
->setStatusCode(Response::HTTP_CREATED);
}
private function validateRequest()
{
return request()->validate([
'title'=> 'required|Min:3',
'comment' => 'required'
]);
}
}
I don't seem to know what could e responsible for the error.
instead of request()->user() replace it with auth('api')->user(); or $request->user('api');

Laravel hasManyThrough relationship with League/Fractal

I'm struggling to get my head around a Laravel hasManyThrough and League/Fractal Transformers.
I have three tables:
Countries:
-id (int)
-other fields ...
Cities:
-id (int)
-country_id (int)
-other fields ...
Users
-id (int)
-city_id (string)
-some other ...
I'm trying to access Users relation from Country via Cities table relation, which is working using following Eloquent query:
$countryUsers = Country::with('users')->where('id', $id)->get();
But when I am trying to user $fractal to transform this relation, I'm getting Segmentation fault (core dumped) error.
In Country Controller I have:
class CountryController extends ApiController
{
protected $manager;
function __construct(Manager $manager) {
$this->manager = $manager;
}
public function show(CountryTransformer $countryTransformer, $id) {
$country = Country::find($id);
return $this->respondItem($country, $countryTransformer);
}
public function respondItem($item, $transformer)
{
$this->manager->setSerializer(new CustomArraySerializer());
$resource = new Item($item, $transformer);
$data = $this->manager->createData($resource)->toArray();
return $this->respond($data);
}
In my country model I have:
public function users() {
return $this->hasManyThrough('App\Models\User', 'App\Models\City');
}
public function cities() {
return $this->hasMany('App\Model\City');
}
City Model:
public function country() {
return $this->belongsTo('App\Models\Country');
}
User Model:
public function city()
{
return $this->belongsTo('App\Models\City');
}
and Fractal country transformer:
<?php namespace App\Transformers;
use App\Models\Country;
use League\Fractal\TransformerAbstract;
class CountryTransformer extends TransformerAbstract {
protected $defaultIncludes = [
'users'
];
public function transform(Country $country)
{
return [
'id' => $country->id,
'name' => $country->name,
'code' => $country->code,
'time_zone' => $country->time_zone,
'active' => $country->active,
'status' => $country->status,
'params' => $country->params
];
}
public function includeUsers(Country $country)
{
$users = $country->users;
if ($users) {
return $this->collection($users, new UserTransformer());
}
}
}
If anyone can point me in the right direction I'd really appreciate it. Thanks.

Call to a member function associate() on a non-object

Error Message: http://puu.sh/d4l0F/5b0ac07e68.png
I've even saved the $transportation object before trying to create associations. I've verified that both $transporation, $from and $to are all their respective objects and they are.
I'm sure I'm missing something stupid here but I'm out of ideas.
My code:
class RideBuilder implements RideBuilderInterface
{
public function create(Advance $advance)
{
$ride = new Ride;
if($ride->validate(Input::all())) {
$ride->save();
$to = Location::find(Input::get('dropoffLocation'));
$from = Location::find(Input::get('pickupLocation'));
$transportation = new Transportation;
$transportation->save();
$transportation->transportable()->associate($ride);
$transportation->to()->associate($to);
$transportation->from()->associate($from);
$event = new Event;
$event->start = Input::get('ridePickUpTime');
$event->save();
$event->eventable->save($transportation);
$event->subjectable->save($advance);
}
return $ride;
}
}
Location Model:
class Location extends Elegant
{
protected $table = 'locations';
public $rules = array(
'label' => 'required|min:2',
'street' => 'required',
'city' => 'required',
'state' => 'required',
'type' => 'required',
);
public function advance()
{
return $this->belongsTo('Booksmart\Booking\Advance\Model\Advance');
}
public function locationable()
{
return $this->morphTo();
}
}
Transportation Model:
class Transportation extends Elegant
{
protected $table = 'transportations';
public function event()
{
$this->morphOne('Booksmart\Component\Event\Model\Event');
}
public function start_location()
{
$this->belongsTo('Booksmart\Component\Location\Model\Location', 'start_location');
}
public function end_location()
{
$this->belongsTo('Booksmart\Component\Location\Model\Location', 'end_location');
}
}
I had a similar issue. I made the stupid mistake of not adding the "return" in the relationship method!
Make sure you return the relationship... Obviously this will not work:
public function medicineType()
{
$this->belongsTo('MedicineType', 'id');
}
This is the correct way:
public function medicineType()
{
return $this->belongsTo('MedicineType', 'id');
}
Easy to miss, hard to debug...

Including additional custom attributes in an laravel eloquent response

Model
class Profile extends Eloquent {
public function user(){ return $this->belongsTo('User'); }
public function url(){
return URL::Route('profile', array('city_slug' => $this->city_slug, 'slug'=> $this->slug));
}
}
Controller
class ProfileController extends BaseController {
public function show($user_id)
{
$profile = Profile::with('user')->where('user_id', $user_id);
return Response::json($profile, 200);
}
}
I would like the response to include the generated URL from the Profile method url(){}
{
"id" : 1,
"url" : /profile/{city_slug}/{slug},
....
}
Thanks in advance.
I would go at this the other way.
In your Users model...
public function profile()
{
return $this->belongsTo('Profiles','profile_id');
}
Then you can use $profile = User::find(user_id)->profile.
$data = array(
'id' => $profile->id,
'url' => "/profile/".$profile->city_slug."/".profile->slug
);
And then finally
return Repsonse::json($data,200);

Resources