I'm developing a RESTful service with Symfony2, JMS Serializer Bundle, FOS Rest Bundle and Hateoas Bundle. There are 2 entities User and Company and I want to, when I serialize a Company get larger detail.
But, when serializing User related Company show only Company ID and name object or just ID as integer.
I have serialize policy like below.
User
Acme\UserBundle\Entity\User:
exclusion_policy: ALL
xml_root_name: user
properties:
id:
expose: true
type: integer
company:
expose: true
type: Acme\CompanyBundle\Entity\Company
name:
expose: true
type: string
surname:
expose: true
type: string
picture:
expose: true
type: string
relations:
-
rel: self
href:
route: acme_v1_get_user
parameters:
id: expr(object.getId())
absolute: true
Company
Acme\CompanyBundle\Entity\Company:
exclusion_policy: ALL
xml_root_name: company
properties:
id:
expose: true
type: integer
name:
expose: true
type: string
address:
expose: true
type: string
phone:
expose: true
type: string
web:
expose: true
type: string
created_date:
expose: true
type: DateTime
updated_date:
expose: true
type: DateTime
status:
expose: true
type: integer
relations:
-
rel: self
href:
route: acme_v1_get_company
parameters:
id: expr(object.getId())
absolute: true
Expected output
{
"id": 1,
"name": "Jenny",
"surname": "Doe",
"picture": "http://google.com/kittens.jpg",
"info": [],
"company": {
"id": 1,
"name": "Demo Company"
}
}
OR
{
"id": 1,
"name": "Jenny",
"surname": "Doe",
"picture": "http://google.com/kittens.jpg",
"info": [],
"company": 1
}
What I got
{
"id": 1,
"name": "Jenny",
"surname": "Doe",
"picture": "http://google.com/kittens.jpg",
"info": [],
"company": {
"id": 1,
"name": "Demo Company",
"address": "Lorem ipsum dolor sit amet",
"phone": "0902124440444",
"web": "http://www.demo-company.com",
"created_date": "2015-07-22T11:21:03+0300",
"updated_date": "2015-07-24T01:50:39+0300",
"status": 1
}
}
You can use groups
AppBundle\Entity\User\User:
exclusion_policy: ALL
properties:
lastname:
expose: true
groups: [info]
And with an annotation, you can define which property is displayed on which group. And finally, you can assign a group to every route you use.
Or you can use virtual properties like so :
AppBundle\Entity\User\User:
exclusion_policy: ALL
properties:
[…]
virtual_properties:
getCompanyId:
serialized_name: company
type: string
groups: [info]
And you create a getCompanyId() method in your User entity, that returns the companyId
The more Hateoas way to do it would be with the relations.
Acme\UserBundle\Entity\User:
exclusion_policy: ALL
xml_root_name: user
properties:
id:
expose: true
type: integer
name:
expose: true
type: string
surname:
expose: true
type: string
picture:
expose: true
type: string
relations:
-
rel: self
href:
route: acme_v1_get_user
parameters:
id: expr(object.getId())
absolute: true
-
rel: company
href:
route: acme_v1_get_company
parameters:
id: expr(object.getCompany().getId())
absolute: true
Would yield...
{
"id": 1,
"name": "Jenny",
"surname": "Doe",
"picture": "http://google.com/kittens.jpg",
"info": []
"_links": {
"self": {
"href": "http://server.com/api/user/1"
},
"company": {
"href": "http://server.com/api/company/1"
},
}
}
Related
I am able to create indexes 1 by 1 but my purpose is to have 1 big index with subindexes to search. (I am reproducing an existing app but upgrading dependencies)
Symfony : 5.4*
FOS-Elastica : 6.1
Elastic : 7*
This is my error message and if I change types to properties I have similar errors. I have been trying to switch and indent differently all day:
Unrecognized option "types" under "fos_elastica.indexes.app". Available options are "_id", "_routing", "_source", "analyzer", "client", "date_detection", "dynamic", "dynamic_date_formats", "dynamic_templates", "finder", "index_
name", "index_prototype", "indexable_callback", "numeric_detection", "persistence", "properties", "serializer", "settings", "use_alias".
What am I doing wrong please ?
#app/config/config.yml
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
app:
settings:
analysis:
analyzer:
fr:
tokenizer: standard
filter: [ lowercase, stop_fr, snowball_fr ]
autocomplete:
type: custom
tokenizer: whitespace
filter: [ lowercase, engram, elision ]
csv:
type: pattern
pattern: '\s*,\s*'
lowercase: false
filter:
snowball_fr:
type: "snowball"
language: "French"
stop_fr:
type: "stop"
stopwords: "_french_"
engram:
type: edge_ngram
min_gram: 2
max_gram: 15
types:
# ---------
# USER
# ---------
user:
properties:
username: ~
email: ~
organization:
type: object
properties:
id: { index: true }
code: { index: true }
name:
index: true
type: text
fields:
source: { type: text, index: true }
persistence:
driver: orm # orm, mongodb, propel are available
model: App\Entity\User
provider: ~
listener: ~
finder: ~
So I was also figuring out how to deal with the same issue as yours, and I do confirm lot of the docs and blogposts out there use the config struture you posted. However when I went back to the v6.1 documentation of the bundle I found this:
Dynamic templates
Dynamic templates allow to define mapping templates
that will be applied when dynamic introduction of fields / objects
happens.
Documentation
fos_elastica:
indexes:
user:
dynamic_templates:
my_template_1:
match: apples_*
mapping:
type: float
my_template_2:
match: *
match_mapping_type: text
mapping:
type: keyword
properties:
username: { type: text }
So in your case a working config would look like:
#app/config/config.yml
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
user:
settings:
analysis:
analyzer:
fr:
tokenizer: standard
filter: [ lowercase, stop_fr, snowball_fr ]
autocomplete:
type: custom
tokenizer: whitespace
filter: [ lowercase, engram, elision ]
csv:
type: pattern
pattern: '\s*,\s*'
lowercase: false
filter:
snowball_fr:
type: "snowball"
language: "French"
stop_fr:
type: "stop"
stopwords: "_french_"
engram:
type: edge_ngram
min_gram: 2
max_gram: 15
properties:
username: ~
email: ~
organization:
type: object
properties:
id: { index: true }
code: { index: true }
name:
index: true
type: text
fields:
source: { type: text, index: true }
persistence:
driver: orm # orm, mongodb, propel are available
model: App\Entity\User
provider: ~
listener: ~
finder: ~
I already have the ElasticBeanstalk (Multicontainer) template. In addition to that, I want to get Docker image from ECR which I already have ****.dkr.ecr.ap-south-1.amazonaws.com/traveltouch:latest in my ECR repo. (If want to create new repo also fine). I mentioned that image in my Dockerrun.json and Dynamodb also needed. Here how to attach that permission and add My ECR repo and new dynamodb table into to my MyInstanceProfile and the cloudformation.
AWSTemplateFormatVersion: '2010-09-09'
Resources:
sampleApplication:
Type: AWS::ElasticBeanstalk::Application
Properties:
Description: AWS Elastic Beanstalk Sample Application
sampleApplicationVersion:
Type: AWS::ElasticBeanstalk::ApplicationVersion
Properties:
ApplicationName:
Ref: sampleApplication
Description: AWS ElasticBeanstalk Sample Application Version
SourceBundle:
S3Bucket: !Sub "elasticbeanstalk-ap-south-1-182107200133"
S3Key: TravelTouch/Dockerrun.aws.json
sampleConfigurationTemplate:
Type: AWS::ElasticBeanstalk::ConfigurationTemplate
Properties:
ApplicationName:
Ref: sampleApplication
Description: AWS ElasticBeanstalk Sample Configuration Template
OptionSettings:
- Namespace: aws:autoscaling:asg
OptionName: MinSize
Value: '2'
- Namespace: aws:autoscaling:asg
OptionName: MaxSize
Value: '6'
- Namespace: aws:elasticbeanstalk:environment
OptionName: EnvironmentType
Value: LoadBalanced
- Namespace: aws:autoscaling:launchconfiguration
OptionName: IamInstanceProfile
Value: !Ref MyInstanceProfile
SolutionStackName: 64bit Amazon Linux 2018.03 v2.26.0 running Multi-container Docker 19.03.13-ce (Generic)
sampleEnvironment:
Type: AWS::ElasticBeanstalk::Environment
Properties:
ApplicationName:
Ref: sampleApplication
Description: AWS ElasticBeanstalk Sample Environment
TemplateName:
Ref: sampleConfigurationTemplate
VersionLabel:
Ref: sampleApplicationVersion
MyInstanceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
Description: Beanstalk EC2 role
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AWSElasticBeanstalkWebTier
- arn:aws:iam::aws:policy/AWSElasticBeanstalkMulticontainerDocker
- arn:aws:iam::aws:policy/AWSElasticBeanstalkWorkerTier
MyInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref MyInstanceRole
and my Dockerrun.json
{
"AWSEBDockerrunVersion": 2,
"containerDefinitions": [{
"environment": [{
"name": "POSTGRES_USER",
"value": "admin"
},
{
"name": "POSTGRES_PASSWORD",
"value": "postgres"
},
{
"name": "POSTGRES_DB",
"value": "traveldb"
}
],
"essential": true,
"image": "postgres:12-alpine",
"memory": 300,
"mountPoints": [{
"containerPath": "/var/lib/postgresql/data/",
"sourceVolume": "postgres_data"
}],
"name": "db",
"portMappings": [{
"containerPort": 5432,
"hostPort": 5432
}]
},
{
"essential": true,
"links": [
"db"
],
"name": "web",
"image": "****.dkr.ecr.ap-south-1.amazonaws.com/traveltouch:latest",
"memory": 300,
"portMappings": [{
"containerPort": 80,
"hostPort": 80
}]
}
],
"volumes": [{
"host": {
"sourcePath": "postgres_data"
},
"name": "postgres_data"
}
]
}
easy_admin:
entities:
User:
class: App\Entity\User
disabled_actions: ['new']
avatar_property_path: 'picture'
form:
fields:
- {property: 'rank'}
- { property: 'roles',
type: choice,type_options: { mapped: true, expanded: true, multiple: true},
choices: [role_admin: 'ROLE_ADMIN' , role_user: 'ROLE_USER'] }
this is the code that i wrote and this is the result :
enter image description here
you should do like this instead of form
easy_admin:
entities:
User:
class: App\Entity\User
disabled_actions: ['new']
avatar_property_path: 'picture'
new:
fields:
- {property: 'rank'}
- { property: 'roles',
type: choice,type_options: { mapped: true, expanded: true, multiple: true},
choices: [role_admin: 'ROLE_ADMIN' , role_user: 'ROLE_USER'] }
edit:
fields:
- {property: 'rank'}
- { property: 'roles',
type: choice,type_options: { mapped: true, expanded: true, multiple: true},
choices: [role_admin: 'ROLE_ADMIN' , role_user: 'ROLE_USER'] }
I`m working on API for drupal and I created some reusable components, when I try to "include" components inside paths one of them not showing up
here are my files:
header component
#HEADER (JSONAPI)
header:
required:
- jsonapi
- data
- links
properties:
jsonapi:
required:
- version
- meta
properties:
version:
type: "string"
example: "1.0"
meta:
required:
- links
properties:
self:
required:
- href
properties:
href:
type: "string"
example: "http://jsonapi.org/format/1.0/"
type: object
type: object
type: object
type: object
#END OF HEADER
links component
#Links
links:
required:
- self
properties:
self:
required:
- href
properties:
href:
type: "string"
example: "http://localhost/jsonapi/link/be336992-26b9-448e-af00-2fb55642d933"
type: object
type: object
data component
#Data Link
data:
type: "array"
items:
type: "object"
properties:
type:
type: "string"
example: "node--link"
id:
type: "string"
example: "12c800a8-ee1d-4fde-9bf6-9479a57f9588"
attributes:
required:
- "drupal_internal__nid"
- "drupal_internal__vid"
- "status"
- "title"
- "created"
- "path"
- "body"
- "url"
properties:
drupal_internal__nid:
type: "number"
example: 8
drupal_internal__vid:
type: "number"
example: 8
status:
type: "boolean"
example: true
title:
type: "string"
example: "Test Link"
created:
type: "string"
example: "2019-12-04T10:11:58+00:00"
path:
required:
- "alias"
- "pid"
- "langcode"
properties:
alias:
type: "string"
example: "/link/test-link"
pid:
type: "number"
example: 8
langcode:
type: "string"
example: "en"
type: "object"
body:
required:
- "value"
- "format"
- "processed"
- "summary"
properties:
value:
type: "string"
example: "<p>This is a test link</p>\r\n"
format:
type: "string"
example: "basic_html"
processed:
type: "string"
example: "<p>This is a test link</p>"
summary:
type: "string"
example: ""
type: "object"
url:
type: "string"
example: "http://www.example.com"
type: "object"
relationships:
required:
- "node_type"
- "uid"
- "link"
properties:
node_type:
required:
- "data"
- "links"
properties:
data:
required:
- "type"
- "id"
properties:
type:
type: "string"
example: "node_type"
id:
type: "string"
example: "a95e770e-9035-4b8c-8c80-679ad9703174"
type: "object"
links:
required:
- "self"
- "related"
properties:
self:
required:
- "href"
properties:
href:
type: "string"
example: "http://localhost/jsonapi/link/12c800a8-ee1d-4fde-9bf6-9479a57f9588/relationships/node_type?resourceVersion=id%3A8"
type: "object"
related:
required:
- "href"
properties:
href:
type: "string"
example: "http://localhost/jsonapi/link/12c800a8-ee1d-4fde-9bf6-9479a57f9588/node_type?resourceVersion=id%3A8"
type: "object"
type: "object"
type: "object"
uid:
required:
- "data"
- "links"
properties:
links:
required:
- "self"
properties:
self:
required:
- "href"
properties:
href:
type: "string"
example: "http://localhost/jsonapi/link/12c800a8-ee1d-4fde-9bf6-9479a57f9588/relationships/uid?resourceVersion=id%3A8"
type: "object"
type: "object"
type: "object"
link:
required:
- "data"
- "links"
properties:
links:
required:
- "self"
properties:
self:
required:
- "href"
properties:
href:
type: "string"
example: "http://localhost/jsonapi/link/12c800a8-ee1d-4fde-9bf6-9479a57f9588/relationships/link?resourceVersion=id%3A8"
type: "object"
type: "object"
type: "object"
type: "object"
#end of data for link
here is my path:
paths:
/link:
get:
responses:
'200':
description: OK
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/header'
- $ref: '#/components/schemas/data'
- $ref: '#/components/schemas/links'
In swagger app only header and links are showing up, data component is missing
Sorry for posting such a long file
The problem is incompatible subschema types in allOf: the header and links schemas are objects but data is an array. This part:
allOf:
- $ref: '#/components/schemas/header'
- $ref: '#/components/schemas/data'
- $ref: '#/components/schemas/links'
is equivalent to:
allOf:
- # header
type: object
properties:
jsonapi: ...
- # data
type: array # <-----
items: ...
- # links
type: object
properties:
self: ...
which is an impossible schema because an instance cannot be an object and an array at the same time.
Assuming the data array is supposed to be wrapped into the data property, you need to change the data schema as follows:
components:
schemas:
data:
type: object
properties:
data:
type: array
items:
...
In my symfony2 application, I am using FOS Elastica bundle to perform searches.
I have tried to set up analyzers and filters but it seems they just have no effect. For instance, if I search for the word 'cake', the objects containing the sentence case 'Cake' won't be returned.
How can I configure those analyzers and filters correctly ?
My config :
#Elastic Search
fos_elastica:
default_manager: orm
clients:
default: { host: localhost, port: 9200 }
indexes:
website:
client: default
settings:
index:
analysis:
analyzer:
custom_index_analyzer :
type : custom
tokenizer: nGram
filter : [stopwords, asciifolding ,lowercase, snowball, elision, worddelimiter]
custom_search_analyzer :
type : custom
tokenizer: nGram
filter : [stopwords, asciifolding ,lowercase, snowball, elision, worddelimiter]
tokenizer:
nGram:
type: nGram
min_gram: 1
max_gram: 2
filter:
snowball:
type: snowball
language: French
elision:
type: elision
articles: [l, m, t, qu, n, s, j, d]
stopwords:
type: stop
stopwords: [_french_]
ignore_case : true
worddelimiter :
type: word_delimiter
index_name: foodmeup
types:
recipe:
mappings:
name:
boost: 5
index_analyzer : custom_index_analyzer
search_analyzer : custom_search_analyzer
nickName:
index_analyzer : custom_index_analyzer
search_analyzer : custom_search_analyzer
content:
index_analyzer : custom_index_analyzer
search_analyzer : custom_search_analyzer
recipeIngredients:
type: "nested"
properties:
name:
index_analyzer : custom_index_analyzer
search_analyzer : custom_search_analyzer
product:
type: "nested"
properties:
name: { type: string, boost: 10}
nickName: { type: string }
content: { type: string }
tags:
type: "nested"
boost: 5
properties:
name: { type: string }
userRecipes:
type: "nested"
properties:
name:
index_analyzer : custom_index_analyzer
search_analyzer : custom_search_analyzer
content:
index_analyzer : custom_index_analyzer
search_analyzer : custom_search_analyzer
tags:
type: "nested"
boost: 5
properties:
name:
index_analyzer : custom_index_analyzer
search_analyzer : custom_search_analyzer
persistence:
driver: orm
model: AppBundle\Entity\FoodAnalytics\Recipe
repository: AppBundle\Repository\FoodAnalytics\RecipeRepository
provider: ~
finder: ~
listener: ~ # by default, listens to "insert", "update" and "delete"
product:
mappings:
name: { type: string, boost: 10}
nickName: { type: string }
content: { type: string }
userIngredients:
type: "nested"
properties:
name: { type: string }
content: { type: string }
tags:
type: "nested"
boost: 5
properties:
name: { type: string }
persistence:
driver: orm
model: AppBundle\Entity\MarketPlace\Product
repository: AppBundle\Repository\MarketPlace\ProductRepository
provider: ~
finder: ~
listener: ~ # by default, listens to "insert", "update" and "delete"
user:
mappings:
firstName: { type: string, boost: 3}
lastName: { type: string, boost: 10 }
content: { type: string }
username: { type: string }
email: { type: string }
jobSeeker:
type: "nested"
properties:
skills:
type: "nested"
properties:
name: { type: string }
experiences:
type: "nested"
properties:
position:
type: "nested"
properties:
name: { type: string }
content: { type: string }
trainings:
type: "nested"
properties:
name: { type: string }
content: { type: string }
diploma:
type: "nested"
properties:
name: { type: string }
persistence:
driver: orm
model: AppBundle\Entity\User\User
repository: AppBundle\Repository\User\UserRepository
provider: ~
finder: ~
listener: ~ # by default, listens to "insert", "update" and "delete"
organization:
mappings:
name: { type: string, boost: 10}
legalName: { type: string, boost: 10}
shortDescription: { type: string, boost: 3}
route: { type: string}
content: { type: string }
persistence:
driver: orm
model: AppBundle\Entity\User\Organization
repository: AppBundle\Repository\User\OrganizationRepository
provider: ~
finder: ~
listener: ~ # by default, listens to "insert", "update" and "delete"
offer:
mappings:
name: { type: string, boost: 10}
content: { type: string }
responsibilities: { type: string }
skills:
type: "nested"
properties:
name: { type: string }
contractType:
type: "nested"
properties:
name: { type: string }
position:
type: "nested"
properties:
name: { type: string, boost: 10 }
persistence:
driver: orm
model: AppBundle\Entity\Job\Offer
repository: AppBundle\Repository\Job\OfferRepository
provider: ~
finder: ~
listener: ~
post:
mappings:
name: { type: string, boost: 10}
content: { type: string }
summary: { type: string }
tags:
type: "nested"
boost: 5
properties:
name: { type: string }
comments:
type: "nested"
properties:
content: { type: string }
persistence:
driver: orm
model: AppBundle\Entity\Social\Post
repository: AppBundle\Repository\Social\PostRepository
provider: ~
finder: ~
listener: ~
The query is a basic one :
$finder = $this->container->get('website.recipe')
$elasticaResults = $finder->find($search);
Each time you update your fos_elastica config file, you need in a first time to update the index(es) with the command:
php app/console fos:elastica:populate --index website --type= <<you type>>
This command will reindex your data with erasing previous one. In a second time, you should have to check your mapping information to check everything is fine and synchronous.
You could check your analyser with this command:
curl 'http://<< yourdomain >>:9200/website/_analyze?pretty=true&analyzer=custom_french_analyzer' -d "Cake"