Alter child collections of two models in Laravel - laravel

I have this:
$merge = parents::with('children')->get();
which gives me:
[{
"id":1,
"parent_name":"Robert",
"children":[{
"id":5,"name":"Susan","dob":"2010-11-11"},
{"id":7,"name":"Tony","dob":"2014-12-10"}]
}]
I want to add age into children with expected output like this:
[{
"id":1,
"parent_name":"Robert",
"children":[{
"id":5,"name":"Susan","dob":"2010-11-11","age":"10"},
{"id":7,"name":"Tony","dob":"2014-12-10","age":"7"}]
}]
I tried this:
return $merge->map(function ($detail) {
$detail->age = \Carbon\Carbon::parse($detail->dob)->diffInYears();
return $detail;});
it gives me:
[{
"id":1,
"parent_name":"Robert",
"age":0,
"children":[{
"id":5,"name":"Susan","dob":"2010-11-11"},
{"id":7,"name":"Tony","dob":"2014-12-10"}]
}]
I tried:
return $merge->children->map(function ($detail) {
$detail->age = \Carbon\Carbon::parse($detail->dob)->diffInYears();
return $detail;});
It returns
Property [children] does not exist on this collection instance.
Is there any simple way to achieve my goal?

loop on each parent and its children and add new age property
$data = parents::with('children')->get();
$data->each(function($parent) {
$parent->children->each(function($child){
$child->age = \Carbon\Carbon::parse($child->dob)->diffInYears();
});
});
what I suggest and I think is better is to add an attribute to your Model like so:
class modelName extends Model{
protected $append = ['age'];
public function getAgeAttribute(){
return \Carbon\Carbon::parse($this->dob)->diffInYears();
}
}

Loop through parents and its children. Eventually calculate the age and put the value to array.
$merge->map(function ($mer) {
$mer->children->map(function ($child) {
$age = \Carbon\Carbon::parse($child->dob)->diffInYears();
$child->age = $age;
});
});

Related

Hoist child object into parent

Is there any way to reach down into the attributes of an object returned by a query?
So imagine that I have an object defined as:
{
id: string
myCollection(someArgs):collection
}
where collection is defined as:
{
metaArg1: int,
metaArg2: int,
...
metaArgn: int,
theArray: [objectType]
}
Now, I have a query like:
query myQuery{
fetchData(){
id,
myCollection(someArgs){
theArray {
...some attribute list
}
}
}
}
Which will return something like this:
{
id: 'id'
myCollection: {
theArray: [{...}, {...}, {...}]
}
}
If I just want the content of theArray, is there any way I can hoist its value one level up?
Essentially, I want to end up with:
{
id: 'id'
myCollection: [{...}, {...}, {...}] // This would be the value previously inside theArray
}
Is there any way to rewrite myQuery to accomplish that?

Laravel - Assert json array ids using wildcard

In my application I have a response like this:
{
"items": [
{
"id": 10,
"field": "foo"
},
{
"id": 20,
"field": "bar"
}
]
}
I need to test the content of items and validate each id.
I've tried many solutions but no one works, for example (this is just a kind of pseudo-code):
assertJson(fn (AssertableJson $json) =>
$json->where('items.*.id', [10, 20])
)
Is there a way to use a wildcard to pick every ID and validate using an array?
You can use array_filter:
$idArray = [10, 20];
$myObj = json_decode($json); // Turn JSON to obj
$items = $myObj["items"]; // Get items from object
// Filter the items for items that aren't in the ID list
$invalidItems = array_filter($items, function ($el) {
// If the item has an id which isn't in the array, return true
return !in_array($el["id"], $idArray);
});
// This returns true if we found 0 items with IDs not in the ID list
return $invalidItems == [];
You can similarly use array_map to simplify your array, then compare it to your ID array:
$myObj = json_decode($json); // Turn JSON to obj
$items = $myObj["items"]; // Get items from object
$outIdArray = array_map(function($el) {
return $el["id"];
}, $items);
// Compare $outIdArray to [10, 20]
Not tested yet but below should work.
We attach an each on each child element under items and add a callback to where on that id key of each child.
<?php
assertJson(fn (AssertableJson $json) =>
$json->each('items', fn (AssertableJson $childJson) =>
$childJson->where('id', fn($idVal) =>
in_array($idVal, [10,20])
)
)
)

Dynamically create pages with Gatsby based on many Contentful references

I am currently using Gatsby's collection routes API to create pages for a simple blog with data coming from Contentful.
For example, creating a page for each blogpost category :
-- src/pages/categories/{contentfulBlogPost.category}.js
export const query = graphql`
query categoriesQuery($category: String = "") {
allContentfulBlogPost(filter: { category: { eq: $category } }) {
edges {
node {
title
category
description {
description
}
...
}
}
}
}
...
[React component mapping all blogposts from each category in a list]
...
This is working fine.
But now I would like to have multiple categories per blogpost, so I switched to Contentful's references, many content-type, which allows to have multiple entries for a field :
Now the result of my graphQL query on field category2 is an array of different categories for each blogpost :
Query :
query categoriesQuery {
allContentfulBlogPost {
edges {
node {
category2 {
id
name
slug
}
}
}
}
}
Output :
{
"data": {
"allContentfulBlogPost": {
"edges": [
{
"node": {
"category2": [
{
"id": "75b89e48-a8c9-54fd-9742-cdf70c416b0e",
"name": "Test",
"slug": "test"
},
{
"id": "568r9e48-t1i8-sx4t8-9742-cdf70c4ed789vtu",
"name": "Test2",
"slug": "test-2"
}
]
}
},
{
"node": {
"category2": [
{
"id": "75b89e48-a8c9-54fd-9742-cdf70c416b0e",
"name": "Test",
"slug": "test"
}
]
}
},
...
Now that categories are inside an array, I don't know how to :
write a query variable to filter categories names ;
use the slug field as a route to dynamically create the page.
For blogposts authors I was doing :
query authorsQuery($author__slug: String = "") {
allContentfulBlogPost(filter: { author: { slug: { eq: $author__slug } } }) {
edges {
node {
id
author {
slug
name
}
...
}
...
}
And creating pages with src/pages/authors/{contentfulBlogPost.author__slug}.js
I guess I'll have to use the createPages API instead.
You can achieve the result using the Filesystem API, something like this may work:
src/pages/category/{contentfulBlogPost.category2__name}.js
In this case, it seems that this approach may lead to some caveats, since you may potentially create duplicated pages with the same URL (slug) because the posts can contain multiple and repeated categories.
However, I think it's more succinct to use the createPages API as you said, keeping in mind that you will need to treat the categories to avoid duplicities because they are in a one-to-many relationship.
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const result = await graphql(`
query {
allContentfulBlogPost {
edges {
node {
category2 {
id
name
slug
}
}
}
}
}
`)
let categories= { slugs: [], names: [] };
result.data.allContentfulBlogPost.edges.map(({node}))=> {
let { name, slug } = node.category2;
// make some checks if needed here
categories.slugs.push(slug);
categories.names.push(name);
return new Set(categories.slugs) && new Set(categories.names);
});
categories.slugs.forEach((category, index) => {
let name = categories.names[index];
createPage({
path: `category/${category}`,
component: path.resolve(`./src/templates/your-category-template.js`),
context: {
name
}
});
});
}
The code's quite self-explanatory. Basically you are defining an empty object (categories) that contains two arrays, slugs and names:
let categories= { slugs: [], names: [] };
After that, you only need to loop through the result of the query (result) and push the field values (name, slug, and others if needed) to the previous array, making the needed checks if you want (to avoid pushing empty values, or that matches some regular expression, etc) and return a new Set to remove the duplicates.
Then, you only need to loop through the slugs to create pages using createPage API and pass the needed data via context:
context: {
name
}
Because of redundancy, this is the same than doing:
context: {
name: name
}
So, in your template, you will get the name in pageContext props. Replace it with the slug if needed, depending on your situation and your use case, the approach is exactly the same.

How to remove the nested input object in the Graphene Django mutation query (Relay)?

I want to create a Mutation in Relay. I'm using InputObjectType pattern to separate the input and make it reusable.
In mutation class I'm using Input class and there I'm passing the InputObjectType
In general it works but the final query at the client side is very ugly.
I need to pass arguments in this way
query( input : { input : { ...arguments } } )
and to be honest I don't like it. I think it looks ugly.
So the question is: Is it possible to avoid to use a lot of these input objects?
It's ok to use 1 input object, but the nested one is redundant and I'd like to avoid to use it.
Thanks for any help!
Here is the example
class FuelTypeInput(graphene.InputObjectType):
id = graphene.Int()
label = graphene.String()
class FuelSubtypeInput(graphene.InputObjectType):
id = graphene.ID()
label = graphene.String()
fuel_type = graphene.Field(FuelTypeInput)
class CreateFuelSubType(relay.ClientIDMutation):
class Input:
input = FuelSubtypeInput(required=True)
fuel_subtype = Field(FuelSubTypeNode)
ok = graphene.Boolean()
def mutate_and_get_payload(root, info, input):
label = input.label
fuel_type = FuelType.objects.get(pk=input.fuel_type.id)
fuel_subtype = FuelSubType(label=label, fuel_type=fuel_type)
ok = True
return CreateFuelSubType(fuel_subtype=fuel_subtype, ok=ok)
The mutation query is:
mutation MyMutations {
createFuelSubtype( input: { input : { label: "Mutation Subtype", fuelType: {
id: 3
}} } ) {
fuelSubtype {
label
}
ok
}
}
It works fine, here is the result. But I'd like to remove the nested input things
{
"data": {
"createFuelSubtype": {
"fuelSubtype": {
"label": "Mutation Subtype"
},
"ok": true
}
}
}
you can fix with this:
class FuelTypeInput(graphene.AbstractType):
id = graphene.Int()
label = graphene.String()
class CreateFuelSubType(relay.ClientIDMutation):
Input = FuelSubtypeInput
fuel_subtype = Field(FuelSubTypeNode)
ok = graphene.Boolean()
# Other Code ...

updating an array of nested documents rethinkdb

I have a document schema like this:
{
"name":"",
"type":"",
"posts":[
{
"url":"",
"content":"",
...
},
{
"url":"",
"content":"",
...
}
...
]
}...
I forgot to create id's for each post on insertion in database. So i'm trying to create a query for that:
r.db('test').table('crawlerNovels').filter(function (x){
return x.keys().contains('chapters')
}).map(function (x){
return x('chapters')
}).map(
function(x){
return x.merge({id:r.uuid()})
}
)
instead this query return all posts with an id but doesn't actually update in the database. I tried using a forEach instead of a map function at the end this doesn't work
After lots of tweaking and frustration i figured it out:
r.db('test').table('crawlerNovels').filter(function (x){
return x.keys().contains('chapters')
}).update(function(novel){
return {"chapters":novel('chapters').map(
function(chapter){
return chapter.merge({"id":r.uuid()})
})}
},{nonAtomic:true})

Resources