I want to extend a directive #modelClass from the lighthouse library.
I'm working on a modular architecture and I don't have an Eloquent Model, I have several, and I'm extending my first Model version, that's why I'm using an interface to bind the last Model that I need.
What I need to do it is to use an interface instead a model class to resolve my type object.
Using the directive #modelClass should looks like this:
type User #modelClass(class: "App\\Models\\versionC\\User") {
id: Int!
username: String!
}
Since I have this binding:
$this->app->bind(UserInterface::class, User::class)
I should have something like:
type User #modelClass(interface: "App\\Interfaces\\UserInterface") {
id: Int!
username: String!
}
But I cannot override or extend the #modelClass directive.
The solution I found it was manipulate the schema
type User #entity(interface: "App\\Interfaces\\UserInterface") {
id: Int!
username: String!
}
class EntityDirective extends BaseDirective implements TypeManipulator
{
public static function definition(): string
{
return /** #lang GraphQL */ <<<'SDL'
"""
Map an interface model class to an object type.
This can be used when an eloquent model it is bind to an interface.
"""
directive #entity(
"""
The interface class name of the corresponding model binding.
"""
interface: String!
) on OBJECT
SDL;
}
public function manipulateTypeDefinition(DocumentAST &$documentAST, TypeDefinitionNode &$objectType)
{
$modelClass = $this->directiveArgValue('interface');
if(!$modelClass) {
throw new DefinitionException(
"An `interface` argument must be assigned to the '{$this->name()}'directive on '{$this->nodeName()}"
);
}
$modelClass = get_class(resolve($modelClass));
// If the type already exists, we use that instead
if (isset($documentAST->types[$objectType->name->value])) {
$objectType = $documentAST->types[$objectType->name->value];
}
$objectType->directives = ASTHelper::mergeNodeList(
$objectType->directives,
[PartialParser::directive('#modelClass(class: "'.addslashes($modelClass).'")')]
);
$documentAST->setTypeDefinition($objectType);
}
}
for the record I'm using lighthouse v4.10
Related
How can you hide a graghQL field in DGS framework. E.g. as below, what can you use so that title cannot be fetched or isn't visible to the client?
type Show {
title: String
actors: [Actor]
}
I don't think that is possible. However, you can set authentication roles by field, which means that only clients with specific roles are going to be able to fetch data from it.
I am attaching a sample from DGS documentation of how you can do it.
#DgsComponent
public class SecurityExampleFetchers {
#DgsData(parentType = "Query", field = "hello")
public String hello() {
return "Hello to everyone";
}
#Secured("admin")
#DgsData(parentType = "Query", field = "secureGroup")
public String secureGroup() {
return "Hello to admins only";
}
}
I understand that Go is not an object-oriented language but I'm trying to implement a inheritance structure in my Iris controllers as suggested by this article. My main motivation for doing so is to avoid repetition. So far, it has been working for me. Take a look at the following code for example.
// APIController.go (package Controllers)
type APIController struct {
mvc.C
}
func (c *APIController) Post(data map[string][]string) ([]byte, error) {
data_parsed := c.ParseFormData(data)
return json.Marshal(data_parsed)
}
// UserController.go (package Controllers)
type UserController struct {
mvc.C
*APIController
}
func (c *UserController) Post() ([]byte, error) {
return c.APIController.Post(c.Ctx.FormValues())
}
So far so good.
But I'm finding it difficult to replicate the same strategy for Models. This is what I've done so far
// Model.go (package Models)
type Model struct {
Id string `json:"_id"`
Created_at string `json:"created_at"`
Updated_at string `json:"updated_at"`
Deleted_at string `json:"deleted_at"`
}
// implements further set of functions to be used by 'child' models...
// User.go (package Models)
type User struct {
*Model
First_name string `json:"first_name"`
Last_name string `json:"last_name"`
Email string `json:"email"`
Username string `json:"username"`
Password string `json:"password"`
Last_login string `json:"last_login"`
}
// APIController.go (package Controllers)
type APIController struct {
mvc.C
Model Models.Model
}
// UserController.go (package Controllers)
type UserController struct {
mvc.C
*APIController
}
func (c *UserController) Post() ([]byte, error) {
c.APIController.Model = new(Models.User) //WRONG!
return c.APIController.Post(c.Ctx.FormValues())
}
As you can see, the APIController is expecting type Models.Model while UserController is passing *Models.User. The end goal is to have a generic model in APIController that any model from any controller and then is able to call all the functions defined in Models.Model so that I don't have to call those function everywhere.
Is it possible to do so? If not, what might be the best approach to avoid repeating the code?
Update
By using inheritance and single parent model and using that in parent APIController, I want to avoid replicating my filter/CRUD logic. For example, inside UserController, if I want to save a record, then instead of using User.Save(input_data) inside UserController, the Save should ideally be defined inside Models.Model and from APIController, I'm able to call Model.Save(input_data) rather than making the same call from child controllers individually.
First make Model an interface instead of a struct. Have it contain all methods that should be common to all models:
type Model interface {
// Common methods
}
Then, as long as User implements all those methods, you can have
c.APIController.Model = new(Models.User) // Now works!
One of the common methods could be Save. Otherwise make Save a non-method:
func Save(m Model) error {
...
}
I have a simple ASP.NET WebApi ODataV4 controller -
public class FooController : ODataController
{
[HttpPost]
public string Post([FromBody] Foo fooObj)
{
...
}
}
The Foo model is as below and Foo is defined as an EntityType in the EDM model -
public class
{
[Key]
string id {get ; set;}
string name {get; set;}
}
I generated ODataClient for this model and trying to Post a new Foo object -
FooContext container = new FooContext(..);
Foo newFoo = new Foo()
{
name = "Simple foo"
}
container.AddToFoos(newFoo );
When I do this, the 'fooObj' in the Post method is null. However when I set the the 'id' property in the request to a non-null value (even empty string), it seems to work fine -
Foo newFoo = new Foo()
{
id = "",
name = "Simple foo"
}
Any pointers what I am missing here?
If you take a look at your $metadata, the key property "id" is non-nullable. During your OData model registration, you should be able to set it to optional. I had a similar issue but I'm using Microsoft.AspNetCore.OData v7.0.1. I'm not sure what version of OData you are using but the way I did this was during the building of my model:
builder.EntityType<Foo>().Property(p => p.Id).IsOptional();
In order to define easy getters and setters for Parse objects in angular2, I want to extend Parse.Object like so:
const Parse = require('parse').Parse;
export class Test extends Parse.Object {
constructor() {
super('Test');
}
get items():Array<string> {
return super.get('items');
}
set items(value:Array<string>) {
super.set('items', value);
}
}
Parse.Object.registerSubclass('Test', Test);
However, I get the following error:
error TS2507: Type 'any' is not a constructor function type.
I'm learning Symfony following The Book. In the tutorial, I successfully configured prePersist events (to set the createdAt field at insert time).
Now I'm trying to do the same but with YAML files instead of annotations. Here my orm.yml file:
AppBundle\Entity\Chronicle:
type: entity
table: chronicles
id:
id:
type: integer
generator: {strategy: AUTO}
fields:
name:
type: string
length: 256
createdAt:
type: datetime
manyToOne:
creator:
targetEntity: User
inversedBy: chronicles
joinColumn:
name: user_id
referencedColumnName: id
game:
targetEntity: Game
joinColumn:
name: game_id
referencedColumnName: id
oneToMany:
characters:
targetEntity: Character
mappedBy: chronicle
lifeCycleCallbacks:
prePersist: [ setCreatedAtValue ]
And this is a snippet of my entity class:
class Chronicle
{
private $id;
private $name;
private $createdAt;
// Associations
private $game;
private $creator;
private $characters;
// TODO: users or user relationships
// Constructor
public function __construct(User $creator=null) {
$this->characters = new ArrayCollection();
$this->creator = $creator;
}
/**
* Set createdAt at the current time.
*
* (Lifecycle callback)
*/
public function setCreatedAtValue()
{
$this->createdAt = new \DateTime();
}
// ...
}
But the setCreatedAtValue() method is never called. So, I get an exception when I try to insert an object.
I have noticed that when using annotations I have to tell about the existence of lifecycle callbacks with the #ORM\HasLifecycleCallbacks() annotation, but I have not found anywhere an equivalent to that in yml, or if that is needed.
In a tutorial I have found I should register the callback in services.yml, but the tutorial never mention it, and I haven't found it anywhere else.
It will be called if you change "lifeCycleCallbacks" to "lifecycleCallbacks".