Enter a name instead of user id - laravel-6.2

I have 2 tables.
first table name is user.
User_id, User_name
Second table name is Question
Question_id, Question, User_id
I want to see the user name when I call the Question table.
table connection code:
public function user()
{
return $this->belongsTo(User::class);
}
And resource code is:
public function toArray($request)
{
return[
'Question' => $this->Question,
'created_at' => $this->created_at->diffForHumans(),
'user' => $this->user->name
];
}
Question controller show function:
public function show(Question $question)
{
return new QuestionResource($question);
}
The error during operation in the following code:
{
"message": "Class 'App\\Model\\User' not found",
"exception": "Symfony\\Component\\Debug\\Exception\\FatalThrowableError",
"file": "C:\\xampp\\htdocs\\forumapp\\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Eloquent\\Concerns\\HasRelationships.php",
"line": 718,
"trace": [
{
"file": "C:\\xampp\\htdocs\\forumapp\\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Eloquent\\Concerns\\HasRelationships.php",
"line": 179,
"function": "newRelatedInstance",
"class": "Illuminate\\Database\\Eloquent\\Model",
"type": "->"
},

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $table = 'user';
}
Create an Elquoent model class because you have used 'return $this->belongsTo(User::class);' User class here to define elquoent relation.
Hope it will work for you.

Related

Symfony 6 with serializer component - deserialize array of objects

Install:
composer require symfony/property-info
composer require symfony/property-access
1 Create own serializer service like
use Symfony\Component\Serializer\Serializer as SymfonySerializer;
class Serializer
{
private SymfonySerializer $serializer;
public function __construct()
{
$this->serializer = new SymfonySerializer(
[
new ArrayDenormalizer(),
new ObjectNormalizer(null, null, null, new ReflectionExtractor())
], ['json' => new JsonEncoder()]
);
}
public function deserialize(string $data, string $type, string $format, array $context = [])
{
return $this->serializer->deserialize($data, $type, $format, $context);
}
}
Create 3 models: Parent,Owner,User
In your model which you put here:
$parent = $this->serializer->deserialize($request->getContent(), Parent::class, 'json');
to get array of objects you need to have property like:
private array $users = [];
default value is neccessary!
and 3 methods like in this documentation:
https://symfony.com/doc/current/components/property_access.html#writing-to-array-properties
addUser, hasUsers and removeUser
Be carefoul....HAS method name must be plural
This own service will work if deserialized JSON has scalar values, objects and also array of objects:
"owner": {
"firstname": "xxx",
"lastname": "xxxx"
}
"users":[
{
"firstname": "xxx",
"lastname":"yyy"
},
{
"firstname": "zzzz",
"lastname":"wwww"
}
]
Pls click arrow up if this answer is useful. Thanks

API Platform: How to normalize a collection of embedded entities in GraphQL?

I'm trying to make a collection of subresources selectable in GraphQL (with pagination). I'd like to be able to query:
query {
getA(id: '/api/A/1') {
aId
subresources {
totalCount
pageInfo {
endCursor
startCursor
hasNextPage
hasPreviousPage
}
edges {
node {
bId
}
}
}
}
}
and get the result:
{
aId: 1,
subresources: {
"totalCount": XX,
"pageInfo": {
"endCursor": "MQ==",
"startCursor": "MA==",
"hasNextPage": true,
"hasPreviousPage": false
},
edges: [
{
node: {
bId: 11
}
},
{
node: {
bId: 12
}
},
{
node: {
bId: 13
}
}
]
}
}
I'm not using Doctrine at all- I'm using custom data providers. The problem I'm encountering is that even when I return an A entity from DataProvider::getItem() that has an array of B subresources, I get an empty array for subresources in GraphQL. I get the correct data in REST though.
I'm following the instructions given in SymfonyCasts and I found a related API Platform issue, but I'm still having no luck.
I traced through API Platform core and I think it has to do with how the entity is normalized in GraphQL. Specifically, an empty array is returned in ItemNormalizer::normalizeCollectionOfRelations(). However, there's a comment saying "to-many are handled directly by the GraphQL resolver" but I'm not sure what that refers to.
Here's the entity code.
use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
#[ApiResource(
graphql: ['item_query', 'collection_query', 'create', 'update', 'delete'],
collectionOperations: ['get', 'post'],
itemOperations: ['get', 'put', 'patch', 'delete'],
normalizationContext: ['groups' => ['read']],
denormalizationContext: ['groups' => ['write']],
)]
class A {
#[ApiProperty(identifier: true)]
#[Groups(['read', 'write'])]
public ?int $aId = null,
/** #var B[] */
#[ApiProperty(readableLink: true, writableLink: true)]
#[Groups(['read', 'write'])]
public $subresources = []
}
And:
#[ApiResource(
graphql: ['item_query', 'collection_query', 'create', 'update', 'delete'],
collectionOperations: ['get', 'post'],
itemOperations: ['get', 'put', 'patch', 'delete'],
normalizationContext: ['groups' => ['read']],
denormalizationContext: ['groups' => ['write']],
)]
class B {
#[ApiProperty(identifier: true)]
#[Groups(['read', 'write'])]
public ?int $bId = null,
}
My ADataProvider:
public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []): A {
$bs = $this->bDataProvider->getCollection(B::class, null, []);
return new A(123, $bs);
}
My BDataProvider:
/**
* #return ArrayPaginator<B>
*/
public function getCollection(string $resourceClass, string $operationName = null, array $context = []): ArrayPaginator {
return ArrayPaginator::fromList([new B(11), new B(12), new B(13)]);
}
ArrayPaginator implements IteratorAggregate and PaginatorInterface.
Specifically I see this error:
{
"errors": [
{
"debugMessage": "Collection returned by the collection data provider must implement ApiPlatform\\Core\\DataProvider\\PaginatorInterface or ApiPlatform\\Core\\DataProvider\\PartialPaginatorInterface.",
"message": "Internal server error",
"extensions": {
"category": "internal"
},
"locations": [
{
"line": 29,
"column": 5
}
],
"path": [
"a",
"b"
],
"trace": [
{
"file": "/homedir/core/src/GraphQl/Resolver/Stage/SerializeStage.php",
"line": 100,
"call": "ApiPlatform\\Core\\GraphQl\\Resolver\\Stage\\SerializeStage::serializeCursorBasedPaginatedCollection(array(0), array(5), array(6))"
},
TLDR: How does one use annotations (or YAML) to make attributes that are collections of subresources selectable in GraphQL?
Any help/ideas are appreciated, thanks for reading!
Found a solution: the ApiPlatform\Core\DataProvider\SubresourceDataProviderInterface needs to be implemented by the BDataProvider.
It gets used in the ReadStage of api platform's graphql resolver. Surprisingly, it's found nowhere in the REST resolver, so this won't get called on a REST request.
The only method that needs to be implemented is getSubresource(). My basic first implementation looks like this:
public function getSubresource(string $resourceClass, array $identifiers, array $context, string $operationName = null) {
if ($context['collection']) {
return $this->getCollection($resourceClass, $operationName, $context);
}
$id = // get your id from $identifiers;
return $this->getItem($resourceClass, $id, $operationName, $context);
}
This isn't found in the docs unfortunately, but there are a few pulls (1, 2) open to add it.

Symfony + FOSRestBundle - How to allow NULL value to a field configured with a custom form type?

I have a simple Symfony API which uses FOSRestBundle. I have an Exercise entity which contains a field sentences. This field is of type json #ORM\Column(type="json") and is stuffed with some nested json. The entity is persisted in a MySQL database.
I use Symfony forms to validate incoming data from a SPA. Here's the data the SPA sends on the endpoint /exercise:
{
"name": "HEP9H",
"sentences": [
{
"name": "Sentence",
"tirettes": [
{
"chain": null
},
{
"chain": {
"name": "Chain 1"
}
}
]
}
]
}
Once persisted, the API then returns the entity as JSON. It should look exactly the same, except that it has an ID. The issue is that I get this piece of JSON in return:
{
"id": 21,
"name": "HEP9H",
"sentences": [
{
"name": "Sentence",
"tirettes": [
{
"chain": {
"name": null
}
},
{
"chain": {
"name": "Chaîne 1"
}
}
]
}
]
}
As you can see, the problem is that my property "chain": null becomes "chain": {"name": null}. I guess this is due to a bad form type configuration. The data structure changes right after I validate my form and before I persist the entity for the first time.
Here's TiretteType:
class TiretteType extends AbstractType {
public function buildForm ( FormBuilderInterface $builder, array $options ) {
$builder
->add ( 'chain', ChainType::class, [
"required" => false
] );
}
}
And here's ChainType:
class ChainType extends AbstractType {
public function buildForm ( FormBuilderInterface $builder, array $options ) {
$builder->add ( 'name', TextType::class );
}
}
I have no underlying data class and no underlying entity (except the root entity Exercise).
What I've tried so far:
adding "required" => false to the 'chain' field, it doesn't change anything
setting "empty_data" => NULL to the 'chain' field, this also doesn't work and overrides any data to NULL
Am I completely missing something?
Thanks!
I found the answer to my issue. Since my field chain had no underlying data class, the form would simply give me an array with default values if it had a null value as input.
The solution is to use a data transformer (https://symfony.com/doc/current/form/data_transformers.html). I had to check for such an empty structure and if found, return back null instead of the given value.
$builder->get ( 'chain' )->addModelTransformer ( new CallbackTransformer(
function ( $originalInput ) {
return $originalInput;
},
function ( $submittedValue ) {
return $submittedValue["name"] === null ? $submittedValue : null;
}
) );
I don't think checking for null properties is the cleanest way to do this but my case is very simple so I won't spend more time on this one.
Hope this helps someone.

symfony entity createAt and updateAt type is datetime, when i wrote api,the result is bad

I am sorry for the title being so non-descriptive but the question I am asking is way to broad to fit in 10 words.
when i use symfony entity createAt and updateAt type is datetime, when i wrote api,the result is bad.I not want change createAt and updateAt to string type.thanks
entity defiend is like this:
/**
* #var \DateTime
*
* #ORM\Column(name="updateAt", type="datetime", options={"comment":"更新时间"})
*/
private $updateAt;
the method in repository :
public function getList($userId, $isMime)
{
$qb = $this->createQueryBuilder('c')
->select('c.title,c.nodeName as node_name, c.node, c.updateAt as datetime');
if($isMime == 0){
$qb->leftJoin(ApprovalInformation::class,'a','WITH','a.flowId = 11 and a.itemId = c.id');
$qb->where('a.approverId = :uid');
}else{
$qb->where('c.userId = :uid');
}
$qb->setParameter('uid', $userId);
return $qb->getQuery()->getResult();
}
controller
$list = $cardRepo->getList($this->user->getId(), $mime);
return $this->json([
'code' => 1,
'msg' => '获取成功',
'data' => $list
]);
{
"code": 1,
"msg": "获取成功",
"data": [
{
"title": "测试报告",
"node_name": "处长批示",
"node": 11,
"datetime": {
"date": "2019-05-07 19:04:00.000000",
"timezone_type": 3,
"timezone": "Asia/Shanghai"
}
},
{
"title": "测试报告2222",
"node_name": "处长审批",
"node": 2,
"datetime": {
"date": "2019-05-07 19:28:14.000000",
"timezone_type": 3,
"timezone": "Asia/Shanghai"
}
}
]
}
my except result like is:
{
"code": 1,
"msg": "获取成功",
"data": [
{
"title": "测试报告",
"node_name": "处长批示",
"node": 11,
"datetime": "2019-05-07 19:04:00",
},
{
"title": "测试报告2222",
"node_name": "处长审批",
"node": 2,
"datetime": "2019-05-07 19:28:14",
}
]
}
I want to know an efficient solution.thanks
You are using $this->json() in your controller which will serialize your entity using json_encode(). This is what is causing the datetime to be returned like this:
$ php -a
php > echo json_encode(new DateTime());
{"date":"2019-05-07 14:20:22.137677","timezone_type":3,"timezone":"UTC"}
There is multiple ways around this. You can create an array from your data before calling $this->json():
return $this->json(
array_map(
function ($entity) {
return [
'title' => $entity->getTitle(),
...
'updateAt' => $entity->getUpdateAt()->format('Y-m-d H:i:s'),
];
},
$list
)
);
Alternatively you could use the Symfony Serializer: https://symfony.com/doc/current/components/serializer.html#serializing-an-object
If you use the Serializer, make sure you also have the DateTimeNormalizer registered.
Just do it following code in your getUpadateAt function in entity:
return $this->updateAt->format('Y-m-d h:I:s);

class not found when unit testing a custom module

I'm trying to write tests for a custom module I've written on Drupal 8 and keep getting an error and at this point I'm out of ideas. Here is the error:
Error: Class 'Drupal\mypackage\Services\Config\MyClassServiceConfig' not found
The PhpUnit class is under
modules\custom\mypackage\tests\src\Unit\mypackageUserAuthTest
Here is the code
class mypackageUserAuthTest extends UnitTestCase
{
protected $user;
protected $loginService;
public function setUp()
{
parent::setUp();
$this->loginService = new LoginService();
$this->user = [
'username' => 'xxx',
'password' => 'xxx',
'deviceId' => 'xxx',
'some-token' => 'xxx'
];
}
/** #test */
public function that_we_can_authenticate_a_user()
{
$IsUserLoggedIn = $this->loginService->login($this->user['username'], $this->user['password']);
$this->assertTrue($IsUserLoggedIn);
}
Now the method login in loginService code
<?php
namespace Drupal\mypackage\Rest;
use GuzzleHttp\Exception\ClientException;
use Drupal\mypackage\Services\RestServiceFactory;
use Drupal\mypackage\Services\Config\MyClassServiceConfig;
class LoginService
{
public function login($username, $password)
{
$configs = new MyClassServiceConfig(null, "mobile", "v1");
$client = RestServiceFactory::create($configs);
try {
$response = $client->post('login', [
'json' => [
'username' => $username,
'password' => $password,
'deviceId' => 'onepiece',
],
]);
return json_decode($response->getBody(), true);
} catch (ClientException $exception) {
switch ($$exception->getResponse()->getStatusCode()) {
case 402: // This only applies to co members
throw new SubscriptionRequiredException();
case 403:
throw new BlockedAccountException();
case 409:
throw new DuplicateEmailException();
case 410:
throw new PasswordDoesNotExistException();
}
throw $exception;
}
}
}
pwd result on MyClassServiceConfig class directory
/var/www/cms/web/modules/custom/mypackage/src/Services/Config
But it seems to fail on the line $configs = new MyClassServiceConfig(null, "mobile", "v1"); with the previously mentioned error :
1) Drupal\Tests\mypackage\Unit\mypackageUserAuthTest::that_we_can_authenticate_a_user
Error: Class 'Drupal\mypackage\Services\Config\MyClassServiceConfig' not found
Btw, I'm using drupal-project structure (https://github.com/drupal-composer/drupal-project)
So I spent days checking the path but it seemed that the files were not loading so I ended up adding the custom module to autload-dev part composer.json.
"autoload": {
"classmap": [
"scripts/composer/ScriptHandler.php"
],
"files": ["load.environment.php"]
},
"autoload-dev": {
"psr-4": { "Drupal\\mypackage\\": "web/modules/custom/mypackage" }
},
Now at least it seems to load the module as I'm getting an other error related to Drupal Container
\Drupal::$container is not initialized yet. \Drupal::setContainer() must be called with a real container.
It is an old question, the same thing happened to me, as I managed to solve it in my case it was as follows:
In the comment of the class where the tests are carried out, something similar to this should go:
The #coversDefaultClass annotation must go with the namespace of the class to test.
/**
* #coversDefaultClass \Drupal\my_module\MyModuleClassName
* #group my_module
*/
class MyModuleCaseTest extends UnitTestCase {
}
Maybe it will serve someone else

Resources