Unresolvable dependency resolving within Laravel - php

I get the error when calling a custom function I created:
namespace App\Jobs\Device;
class UpdateUserReference {
public $installationReference;
public $userReference;
// assign payload data and installation id received to command
function __construct($bodyContent) {
$this->installationReference = $bodyContent["installationReference"];
$this->userReference = $bodyContent["userReference"];
}
}
Unresolvable dependency resolving [Parameter #0 [ $bodyContent ]] in class App\Jobs\Device\UpdateUserReference
Strange enough I never had the error before and I create my function exactly the same as for all other cases. What is wrong with the bodyContent variable I provide?
The function is called within:
public function update(Request $request) {
// Get payload from request
$bodyContent = json_decode($request->getContent(), true);
$action = $bodyContent["action"];
$installationReference = $bodyContent["installationReference"];
switch($action) {
case 'userReference':
$updateUserReference = new UpdateUserReference($bodyContent);
$result = $this->commandBus->execute($updateUserReference);
break;
}
if (!empty($result)) {
$response = [
'error' => [
'code' => 400,
'message' => "Update Error"
]
];
return $this->setStatusCode(400)->respond($response);
}
$response = [
'data' => [
'installationReference' => $installationReference
]
];
return $this->setStatusCode(201)->respond($response);
}

Related

Symfony 5.4 - how to filter/sanitize/validate request parameter in Rest API action

I am fairly new to Symfony 5.4 and recently created my first API using that version
For my specific API endpoint one of the parameters is an array of IDs.
I need to validate this array in the following way:
make sure that this IS an array;
make sure that IDs in the array actually refer to database records;
I implemented it in a straightforward way where I check the array before persisting the entity using typecasting and existing Repository:
$parentPropertyIds = (array)$request->request->get('parent_property_ids');
if ($parentPropertyIds) {
$parentCount = $doctrine->getRepository(Property::class)->countByIds($parentPropertyIds);
if ($parentCount !== count($parentPropertyIds)) {
return $this->json([
'status' => 'error',
'message' => 'parent_property_id_invalid'
], 422);
}
foreach ($parentPropertyIds as $parentPropertyId) {
$parentProperty = $doctrine->getRepository(Property::class)->find($parentPropertyId);
$property->addParent($parentProperty);
}
}
However, this makes my controller action become too "body-positive" and also feels like something that could be implemented in a more elegant way.
I was unable to find anything in Symfony 5.4 docs.
At the moment I am wondering if:
there is a way to filter/sanitize request parameter available in Symfony;
there is an elegant built-in way to apply custom validator constraint to a request param (similar to well-documented entity field validation);
Full endpoint code:
/**
* #Route("/property", name="property_new", methods={"POST"})
*/
public function create(ManagerRegistry $doctrine, Request $request, ValidatorInterface $validator): Response
{
$entityManager = $doctrine->getManager();
$property = new Property();
$property->setName($request->request->get('name'));
$property->setCanBeShared((bool)$request->request->get('can_be_shared'));
$parentPropertyIds = (array)$request->request->get('parent_property_ids');
if ($parentPropertyIds) {
$parentCount = $doctrine
->getRepository(Property::class)
->countByIds($parentPropertyIds);
if ($parentCount !== count($parentPropertyIds)) {
return $this->json([
'status' => 'error',
'message' => 'parent_property_id_invalid'
], 422);
}
foreach ($parentPropertyIds as $parentPropertyId) {
$parentProperty = $doctrine->getRepository(Property::class)->find($parentPropertyId);
$property->addParent($parentProperty);
}
}
$errors = $validator->validate($property);
if (count($errors) > 0) {
$messages = [];
foreach ($errors as $violation) {
$messages[$violation->getPropertyPath()][] = $violation->getMessage();
}
return $this->json([
'status' => 'error',
'messages' => $messages
], 422);
}
$entityManager->persist($property);
$entityManager->flush();
return $this->json([
'status' => 'ok',
'id' => $property->getId()
]);
}
You could use a combination of Data Transfer Object (DTO) with Validation service. There is a number of predefined constraints or you could create a custom one.
For expamle, how to use simple constraint as an annotation:
class PropertyDTO {
/**
* #Assert\NotBlank
*/
public string $name = "";
public bool $shared = false;
}
Then assign data to DTO:
$propertyData = new PropertyDTO();
$propertyData->name = $request->request->get('name');
...
In some cases it is a good idea to define a constructor in the DTO, then get all data from the request and pass it to DTO at once:
$data = $request->getContent(); // or $request->getArray(); depends on your content type
$propertyData = new PropertyDTO($data);
Then validate it:
$errors = $validator->validate($propertyData);
if (count($errors) > 0) {
/*
* Uses a __toString method on the $errors variable which is a
* ConstraintViolationList object. This gives us a nice string
* for debugging.
*/
$errorsString = (string) $errors;
return $this->json([
'status' => 'error',
'message' => 'parent_property_id_invalid'
], 422);
}
//...

Schema is not configured for mutations in webonyx/graphql-php

i'm new in graphql.
I'm try to config graphql mutation via siler+swoole php framework, that use webonyx/graphql-php.
When i post query i'm get error "Schema is not configured for mutations", but it's configured in my shema.
My index
$typeDefs = file_get_contents(__DIR__.'/schema.graphql');
$resolvers = include __DIR__.'/resolvers.php';
$schema = GraphQL\schema($typeDefs, $resolvers);
GraphQL\execute($schema, GraphQL\request()->toArray(), [], [])
schema.graphql :
schema {
query: Query
mutation: Mutation
}
type Query {
clusters: [Cluster!]!
}
type Cluster {
id: Int
title: String
}
type Mutation {
addCluster(title: String!): Cluster!
}
resolver.php
<?php
use RedBeanPHP\R;
//R::setup('sqlite:'.__DIR__.'/db.sqlite');
$clusters = [
'clusters' => function () {
return R::findAll('clusters');
},
];
$queryType = [
'clusters' => function () {
return R::findAll('clusters');
},
];
$mutationType = [
'addCluter' => function ($root, $args) {
$title = $args['title'];
$cluster = R::dispense('cluster');
$cluster['title'] = $title;
R::store($cluster);
return $cluster;
},
];
return [
'Cluster' => $clusters,
'Query' => $queryType,
'Mutation' => $mutationType,
];
And my query is:
mutation addCluster($clusterName: String) {
addCluster(clusterName: $clusterName) {
id
}
}
The response says:
Schema is not configured for mutations

how to fix 500 internal server error in postman using laravel

service provider detail:
class ServiceProviderDetail extends Model {
protected $fillable = [
'working_status', 'booked', 'title', 'experience',
];
public function user()
{
return $this->belongsTo('App\User', 'user_id');
} }
add_booking:
class BookingController extends Controller {
public function addBooking(Request $request) {
//return $request;
$serviceman = ServiceProviderDetail::where('user_id', $request->service_provider_id)->first();
/********* Check if the serviceman is on duty ********/
if($serviceman->working_status !=1){
return response()->json([
'status'=> 0,
'message' => "Sorry pro has logged off duty",
'data' => []
], 200);
}
$serviceman->booked = '1';
if($request->input('subservice_id') == ''){
$service_id = $request->input('service_id');
} else {
$service_id = $request->input('subservice_id');
}
$booking = new Booking;
$booking->customer_id = $request->input('customer_id');
$booking->service_id = $service_id;
$booking->service_provider_id = $request->input('service_provider_id');
$booking->latitude = $request->input('latitude');
$booking->longitude = $request->input('longitude');
$booking->polyline = $request->input('polyline');
$booking->servicing_address = $request->input('servicing_address');
$booking->service_name = $request->input('service_name');
$booking->service_code = $request->input('service_code');
$booking->service_mobile = $request->input('service_mobile');
$booking->service_email = $request->input('service_email');
$booking->service_date = $request->input('service_date');
$booking->service_time = $request->input('service_time');
$booking->status = $request->input('status');
SendNotification::instance()->sendNotification('You have a new service request.',$booking->service_provider_id);
if($booking->save()) {
$serviceman->save();
return response()->json([
'status'=> 1,
'message' => 'Service booked successfully',
'data' => array(
$booking
),
], 200);
}
}
error_log:
emphasized textproduction.ERROR: Trying to get property 'working_status' of non-object {"exception":"[object] (ErrorException(code: 0): Trying to get property 'working_status' of non-object ...../BookingController.php:25)
[stacktrace]
$serviceman = ServiceProviderDetail::where('user_id', $request->service_provider_id)->first();
This is returning null. check if $serviceman is null before caling values

How can we add status code response when we hit a rest api?

How can we be able to get a status code response if we are not able to pass the data on the api? I need your help guys I am completely new on this. Thanks in advance.
contoller
...
public function user(Request $request) {
$params = [
"firstName" => $request->firstname,
"lastName" => $request->lastname,
];
return $this->VoucherFactory->saveUser($params);
}
...
VoucherFactory.php
...
public function saveUser($params)
{
return $this->HttpClient->makeRequestPost($params, 'api/users', true);
//status code response logic
}
...
I'd do smth like this:
In the factory:
public function saveUser($params)
{
// No params passed, set code to 400
if(empty($params)) {
$statusCode = 400;
} else {
// Params passed, try to save user
$saveUserResult = $this->HttpClient->makeRequestPost($params, 'api/users', true);
// User saved ok
if($saveUserResult) {
$statusCode = 200;
} else {
$statusCode = 400;
}
}
return ["code" => $statusCode];
}
And than in the controller:
public function user(Request $request) {
$params = [
"firstName" => $request->firstname,
"lastName" => $request->lastname,
];
$result = $this->VoucherFactory->saveUser($params);
return $result["code"];
// Or if using some kind of framework, smth like:
// return $this->view(null, $result["code"]);
}
in you saveUser() function,
$this->HttpClient->makeRequestPost($params, 'api/users', true);
//after
return response()->setStatusCode(Response::HTTP_OK);
or in api/users route Controller, return the desired response

Laravel 5.6, where to keep additional classes (additional functional)?

I need to make request on some CRM api in my controller. For making this I have pretty big method. It's look like ugly. I know that there are some "Services" and to put additional code into Service is a good way. But I don't know what is this. Is it a custom classes into app folder? Or maybe it's Service-providers? I have read service-providers documentation and I'm not sure that service-providers is suitable for this. Here is my code:
<?php
namespace App\Http\Controllers;
use App\User;
use App\UserInfo;
use Validator;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$users = User::with('info')
->paginate(20);
$users->withPath(DIRECTORY_SEPARATOR . $request->path() .DIRECTORY_SEPARATOR);
return response()->json($users)->setEncodingOptions(JSON_UNESCAPED_UNICODE);
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$data = $request->json()->all();
$rules = [
'name' => 'required',
'phone' => 'required|unique:users'
];
$validator = Validator::make($data, $rules);
if ($validator->fails()) return response()->json(['errors'=>$validator->errors()]);
$user = new User();
$user->name = request('name');
$user->phone = request('phone');
$user_info_obj = $this->storeUserInfo();
if($user_info_obj === null){
return response('Impassible to define user geo data', 400);
}
$user->info_id = $user_info_obj->id;
$user->save();
$this->makeAMOLead($user->name,
$user->phone,
$user_info_obj->user_agent,
$user_info_obj->city,
$user_info_obj->country);
return response()->json(['success' => 'User created successfully']);
}
public function storeUserInfo()
{
$ip = request()->ip();
$reader = new \GeoIp2\Database\Reader('../resources/geo-lite2-city_20180807/GeoLite2-City.mmdb');
try {
$record = $reader->city($ip);
}
catch (\Throwable $e){
// Code bellow is for testing on localhost, Because of maybe exception instead of geo obj on localhost.
$info = new UserInfo();
$info->ip = '127.0.0.1';
$info->city = 'Some city';
$info->country = 'Some country';
$info->country_code = 'Some code';
$info->continent = 'Some continent';
$info->continent_code = 'no';
$info->user_agent = 'User agent';
$info->save();
return $info;
//return null;
}
$city = $record->city->names['ru'];
$continent = $record->continent->names['ru'];
$continent_code = $record->continent->code;
$country = $record->country->names['ru'];
$country_code = $record->country->isoCode;
$user_agent = \request()->userAgent();
$info = new UserInfo();
$info->ip = $ip;
$info->city = $city;
$info->country = $country;
$info->country_code = $country_code;
$info->continent = $continent;
$info->continent_code = $continent_code;
$info->user_agent = $user_agent;
$info->save();
return $info;
}
private function makeAMOLead($name, $phone, $userAgent, $city, $country)
{
$domain = env('AMO_DOMAIN');
$login = env('AMO_LOGIN');
$hash = env('AMO_HASH');
try {
$credentials = new \ddlzz\AmoAPI\CredentialsManager($domain, $login, $hash);
$settings = new \ddlzz\AmoAPI\SettingsStorage();
$settings->setCookiePath(env('AMO_COOKIE_FILE_PATH'));
$request = \ddlzz\AmoAPI\ClientFactory::create($credentials, $settings);
$lead = new \ddlzz\AmoAPI\Model\Amo\Lead();
$lead['name'] = $name;
if(env('AMO_PIPELINE_ID', null)){
$lead['pipeline_id'] = intval(env('AMO_PIPELINE_ID'));
}
$lead['name'] = 'New pickup user ' . $name;
$lead['custom_fields'] = [
[
'id' => env('AMO_NAME_FIELD_ID'),
'values' => [
['value' => $name],
]
],
[
'id' => env('AMO_USER_AGENT_FIELD_ID'),
'values' => [
['value' => $userAgent]
]
],
[
'id' => env('AMO_CITY_FIELD_ID'),
'values' => [
['value' => $city]
]
],
[
'id' => env('AMO_COUNTRY_FIELD_ID'),
'values' => [
['value' => $country]
]
],
];
$lead['created_at'] = time();
$result = $request->add($lead);
$pipelineId = json_decode($result)->_embedded->items{0}->id;
// create contact
$contact = new \ddlzz\AmoAPI\Model\Amo\Contact();
$contact['name'] = $name;
$contact['created_at'] = time();
$contact['leads_id'] = "$pipelineId";
// dd($request->accountInfo(), true); // Call this, if you need to know ids of default fields (like phone, or position)
$contact['custom_fields'] = [
[
'id' => env('AMO_CONTACT_PHONE_ID'),
'values' => [
[
'value' => $phone,
'enum' => 'MOB',
],
]
],
];
$result = $request->add($contact);
} catch (Exception $e) {
echo response()->json(['error' => $e->getFile() . ': ' . $e->getMessage()]);
}
}
}
Look on the makeAMOLead. This is big method in my controller and this is not ok for controller conception.
Please use repository pattern to split all the communication between the application and your data source. and call the repository functions inside your controller. It is good practice. Here is an article you can understand about that
Example:
Your functions can be separate from controller to repository.
storeUserInfo
makeAMOLeadin
Move your functions an repository and call them into your controller.

Categories