mezzio / mezzio-hal

Hypertext Application Language implementation for PHP and PSR-7

Home Page:https://docs.mezzio.dev/mezzio-hal/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

The Parent-Child (self) relationship is visible only on first page in pagination.

goharsahi opened this issue · comments

Bug Report

The Parent-Child (self) relationship isn't working properly

Q A
Mezzio-Hal ^2.0

Summary

The Parent-Child (self) relationship isn't working properly in pagination.

Current behavior

While listing the parent-child data of an Entity, parent data is visible only on first page. In rest of the pages only original data is visible along with following portion.

"parent": {
          "__initializer__": {},
          "__cloner__": {},
          "__isInitialized__": false
        },

How to reproduce

http://localhost:8080/banks or http://localhost:8080/banks?page=1 lists following data.

GET http://localhost:8080/banks?page=1

HTTP/1.1 200 OK
Date: Sat, 06 Mar 2021 06:41:18 GMT
Server: Apache/2.4.38 (Debian)
X-Powered-By: PHP/7.3.27
Content-Length: 4446
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: application/hal+json

{
  "_total_items": 5,
  "_page": 1,
  "_page_count": 3,
  "_links": {
    "self": {
      "href": "http://localhost:8080/banks/?page=1"
    },
    "next": {
      "href": "http://localhost:8080/banks/?page=2"
    },
    "last": {
      "href": "http://localhost:8080/banks/?page=3"
    }
  },
  "_embedded": {
    "bank": [
      {
        "id": 1,
        "parent_id": 0,
        "children": {},
        "parent": {
          "__initializer__": {},
          "__cloner__": {},
          "__isInitialized__": false
        },
        "name": "Bank #1",
        "phone": "555-555-5555",
        "fax": "666-666-6666",
        "address1": "55 Any Street",
        "address2": "Suite 100",
        "city": "Wellington",
        "zone_id": 18,
        "zip": "33496",
        "product_1_price": "0.00",
        "product_2_price": "0.00",
        "allow_email_attachment": 1,
        "is_active": 1,
        "created": {
          "date": "2011-06-04 00:26:43.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "modified": {
          "date": "2011-12-29 14:19:29.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "_links": {
          "self": {
            "href": "http://localhost:8080/banks/1"
          }
        }
      },
      {
        "id": 2,
        "parent_id": 1,
        "children": {},
        "name": "Bank #2",
        "phone": "555-555-5555",
        "fax": "666-666-6666",
        "address1": "55 Any Street",
        "address2": "Suite 100",
        "city": "Boca Raton",
        "zone_id": 18,
        "zip": "33496",
        "product_1_price": "0.00",
        "product_2_price": "0.00",
        "allow_email_attachment": 0,
        "is_active": 1,
        "created": {
          "date": "2011-06-05 14:33:30.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "modified": {
          "date": "2012-04-03 16:25:40.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "_links": {
          "self": {
            "href": "http://localhost:8080/banks/2"
          }
        },
        "_embedded": {
          "parent": {
            "id": 1,
            "parent_id": 0,
            "children": {},
            "parent": {
              "__initializer__": {},
              "__cloner__": {},
              "__isInitialized__": false
            },
            "name": "Bank #1",
            "phone": "555-555-5555",
            "fax": "666-666-6666",
            "address1": "55 Any Street",
            "address2": "Suite 100",
            "city": "Wellington",
            "zone_id": 18,
            "zip": "33496",
            "product_1_price": "0.00",
            "product_2_price": "0.00",
            "allow_email_attachment": 1,
            "is_active": 1,
            "created": {
              "date": "2011-06-04 00:26:43.000000",
              "timezone_type": 3,
              "timezone": "UTC"
            },
            "modified": {
              "date": "2011-12-29 14:19:29.000000",
              "timezone_type": 3,
              "timezone": "UTC"
            },
            "_links": {
              "self": {
                "href": "http://localhost:8080/banks/1"
              }
            }
          }
        }
      }
    ]
  }
}

Response code: 200 (OK); Time: 6720ms; Content length: 4446 bytes

And http://localhost:8080/banks?page=2 shows following data.

GET http://localhost:8080/banks?page=2

HTTP/1.1 200 OK
Date: Sat, 06 Mar 2021 06:42:26 GMT
Server: Apache/2.4.38 (Debian)
X-Powered-By: PHP/7.3.27
Content-Length: 3169
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: application/hal+json

{
  "_total_items": 5,
  "_page": 2,
  "_page_count": 3,
  "_links": {
    "self": {
      "href": "http://localhost:8080/banks/?page=2"
    },
    "first": {
      "href": "http://localhost:8080/banks/?page=1"
    },
    "prev": {
      "href": "http://localhost:8080/banks/?page=1"
    },
    "next": {
      "href": "http://localhost:8080/banks/?page=3"
    },
    "last": {
      "href": "http://localhost:8080/banks/?page=3"
    }
  },
  "_embedded": {
    "bank": [
      {
        "id": 4,
        "parent_id": 1,
        "children": {},
        "parent": {
          "__initializer__": {},
          "__cloner__": {},
          "__isInitialized__": false
        },
        "name": "New Child Bank",
        "phone": "555-555-5555",
        "fax": "666-666-6666",
        "address1": "55 Any Street",
        "address2": "Suite 100",
        "city": "Wellington",
        "zone_id": 18,
        "zip": "33496",
        "product_1_price": "8.50",
        "product_2_price": "7.80",
        "allow_email_attachment": 1,
        "is_active": 1,
        "created": {
          "date": "2021-03-04 09:29:05.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "modified": {
          "date": "2021-03-04 09:29:05.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "_links": {
          "self": {
            "href": "http://localhost:8080/banks/4"
          }
        }
      },
      {
        "id": 5,
        "parent_id": 1,
        "children": {},
        "parent": {
          "__initializer__": {},
          "__cloner__": {},
          "__isInitialized__": false
        },
        "name": "New Child Bank #2",
        "phone": "555-555-5555",
        "fax": "666-666-6666",
        "address1": "55 Any Street",
        "address2": "Suite 100",
        "city": "Wellington",
        "zone_id": 18,
        "zip": "33496",
        "product_1_price": "8.50",
        "product_2_price": "7.80",
        "allow_email_attachment": 1,
        "is_active": 1,
        "created": {
          "date": "2021-03-04 11:23:58.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "modified": {
          "date": "2021-03-04 11:23:58.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "_links": {
          "self": {
            "href": "http://localhost:8080/banks/5"
          }
        }
      }
    ]
  }
}

Response code: 200 (OK); Time: 6778ms; Content length: 3169 bytes

See the difference of portion starting from

          "parent": {
            "id": 1,
            "parent_id": 0,
            "children": {},
            "parent": {
              "__initializer__": {},
              "__cloner__": {},
              "__isInitialized__": false
            },
...
...

Expected behavior

http://localhost:8080/banks?page=1

GET http://localhost:8080/banks?page=1

HTTP/1.1 200 OK
Date: Sat, 06 Mar 2021 06:41:18 GMT
Server: Apache/2.4.38 (Debian)
X-Powered-By: PHP/7.3.27
Content-Length: 4446
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: application/hal+json

{
  "_total_items": 5,
  "_page": 1,
  "_page_count": 3,
  "_links": {
    "self": {
      "href": "http://localhost:8080/banks/?page=1"
    },
    "next": {
      "href": "http://localhost:8080/banks/?page=2"
    },
    "last": {
      "href": "http://localhost:8080/banks/?page=3"
    }
  },
  "_embedded": {
    "bank": [
      {
        "id": 1,
        "parent_id": 0,
        "children": {},
        "parent": {
          "__initializer__": {},
          "__cloner__": {},
          "__isInitialized__": false
        },
        "name": "Bank #1",
        "phone": "555-555-5555",
        "fax": "666-666-6666",
        "address1": "55 Any Street",
        "address2": "Suite 100",
        "city": "Wellington",
        "zone_id": 18,
        "zip": "33496",
        "product_1_price": "0.00",
        "product_2_price": "0.00",
        "allow_email_attachment": 1,
        "is_active": 1,
        "created": {
          "date": "2011-06-04 00:26:43.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "modified": {
          "date": "2011-12-29 14:19:29.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "_links": {
          "self": {
            "href": "http://localhost:8080/banks/1"
          }
        }
      },
      {
        "id": 2,
        "parent_id": 1,
        "children": {},
        "name": "Bank #2",
        "phone": "555-555-5555",
        "fax": "666-666-6666",
        "address1": "55 Any Street",
        "address2": "Suite 100",
        "city": "Boca Raton",
        "zone_id": 18,
        "zip": "33496",
        "product_1_price": "0.00",
        "product_2_price": "0.00",
        "allow_email_attachment": 0,
        "is_active": 1,
        "created": {
          "date": "2011-06-05 14:33:30.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "modified": {
          "date": "2012-04-03 16:25:40.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "_links": {
          "self": {
            "href": "http://localhost:8080/banks/2"
          }
        },
        "_embedded": {
          "parent": {
            "id": 1,
            "parent_id": 0,
            "children": {},
            "parent": {
              "__initializer__": {},
              "__cloner__": {},
              "__isInitialized__": false
            },
            "name": "Bank #1",
            "phone": "555-555-5555",
            "fax": "666-666-6666",
            "address1": "55 Any Street",
            "address2": "Suite 100",
            "city": "Wellington",
            "zone_id": 18,
            "zip": "33496",
            "product_1_price": "0.00",
            "product_2_price": "0.00",
            "allow_email_attachment": 1,
            "is_active": 1,
            "created": {
              "date": "2011-06-04 00:26:43.000000",
              "timezone_type": 3,
              "timezone": "UTC"
            },
            "modified": {
              "date": "2011-12-29 14:19:29.000000",
              "timezone_type": 3,
              "timezone": "UTC"
            },
            "_links": {
              "self": {
                "href": "http://localhost:8080/banks/1"
              }
            }
          }
        }
      }
    ]
  }
}

Response code: 200 (OK); Time: 6720ms; Content length: 4446 bytes

http://localhost:8080/banks?page=2

GET http://localhost:8080/banks?page=2

HTTP/1.1 200 OK
Date: Sat, 06 Mar 2021 06:42:26 GMT
Server: Apache/2.4.38 (Debian)
X-Powered-By: PHP/7.3.27
Content-Length: 3169
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: application/hal+json

{
  "_total_items": 5,
  "_page": 2,
  "_page_count": 3,
  "_links": {
    "self": {
      "href": "http://localhost:8080/banks/?page=2"
    },
    "first": {
      "href": "http://localhost:8080/banks/?page=1"
    },
    "prev": {
      "href": "http://localhost:8080/banks/?page=1"
    },
    "next": {
      "href": "http://localhost:8080/banks/?page=3"
    },
    "last": {
      "href": "http://localhost:8080/banks/?page=3"
    }
  },
  "_embedded": {
    "bank": [
      {
        "id": 4,
        "parent_id": 1,
        "children": {},
        "parent": {
          "__initializer__": {},
          "__cloner__": {},
          "__isInitialized__": false
        },
        "name": "New Child Bank",
        "phone": "555-555-5555",
        "fax": "666-666-6666",
        "address1": "55 Any Street",
        "address2": "Suite 100",
        "city": "Wellington",
        "zone_id": 18,
        "zip": "33496",
        "product_1_price": "8.50",
        "product_2_price": "7.80",
        "allow_email_attachment": 1,
        "is_active": 1,
        "created": {
          "date": "2021-03-04 09:29:05.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "modified": {
          "date": "2021-03-04 09:29:05.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "_links": {
          "self": {
            "href": "http://localhost:8080/banks/4"
          }
        },
        "_embedded": {
          "parent": {
            "id": 1,
            "parent_id": 0,
            "children": {},
            "name": "Bank #1",
            "phone": "555-555-5555",
            "fax": "666-666-6666",
            "address1": "55 Any Street",
            "address2": "Suite 100",
            "city": "Wellington",
            "zone_id": 18,
            "zip": "33496",
            "product_1_price": "0.00",
            "product_2_price": "0.00",
            "allow_email_attachment": 1,
            "is_active": 1,
            "created": {
              "date": "2011-06-04 00:26:43.000000",
              "timezone_type": 3,
              "timezone": "UTC"
            },
            "modified": {
              "date": "2011-12-29 14:19:29.000000",
              "timezone_type": 3,
              "timezone": "UTC"
            },
            "_links": {
              "self": {
                "href": "http://localhost:8080/banks/1"
              }
            }
          }
        }
      },
      {
        "id": 5,
        "parent_id": 1,
        "children": {},
        name": "New Child Bank #2",
        "phone": "555-555-5555",
        "fax": "666-666-6666",
        "address1": "55 Any Street",
        "address2": "Suite 100",
        "city": "Wellington",
        "zone_id": 18,
        "zip": "33496",
        "product_1_price": "8.50",
        "product_2_price": "7.80",
        "allow_email_attachment": 1,
        "is_active": 1,
        "created": {
          "date": "2021-03-04 11:23:58.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "modified": {
          "date": "2021-03-04 11:23:58.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "_links": {
          "self": {
            "href": "http://localhost:8080/banks/5"
          }
        },
        "_embedded": {
          "parent": {
            "id": 1,
            "parent_id": 0,
            "children": {},
            "parent": {
              "__initializer__": {},
              "__cloner__": {},
              "__isInitialized__": false
            },
            "name": "Bank #1",
            "phone": "555-555-5555",
            "fax": "666-666-6666",
            "address1": "55 Any Street",
            "address2": "Suite 100",
            "city": "Wellington",
            "zone_id": 18,
            "zip": "33496",
            "product_1_price": "0.00",
            "product_2_price": "0.00",
            "allow_email_attachment": 1,
            "is_active": 1,
            "created": {
              "date": "2011-06-04 00:26:43.000000",
              "timezone_type": 3,
              "timezone": "UTC"
            },
            "modified": {
              "date": "2011-12-29 14:19:29.000000",
              "timezone_type": 3,
              "timezone": "UTC"
            },
            "_links": {
              "self": {
                "href": "http://localhost:8080/banks/1"
              }
            }
          }
        }
      }
    ]
  }
}

Response code: 200 (OK); Time: 6778ms; Content length: 3169 bytes

@goharsahi
Unfortunately, the single code snippets are too long to be able to compare anything. I am missing a summary / short example of what the expected behaviour should be.
Maybe you can put the current behaviour and the expected behaviour as short excerpts below each other.
Thanks!

@froschdesign
Ok. Here's current behavior on http://localhost:8080/banks?page=2.

{
        "id": 5,
        "parent_id": 1,
        "children": {},
        "parent": {
          "__initializer__": {},
          "__cloner__": {},
          "__isInitialized__": false
        },
        "name": "New Child Bank #2",
        "phone": "555-555-5555",
        "fax": "666-666-6666",
        "address1": "55 Any Street",
        "address2": "Suite 100",
        "city": "Wellington",
        "zone_id": 18,
        "zip": "33496",
        "product_1_price": "8.50",
        "product_2_price": "7.80",
        "allow_email_attachment": 1,
        "is_active": 1,
        "created": {
          "date": "2021-03-04 11:23:58.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "modified": {
          "date": "2021-03-04 11:23:58.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "_links": {
          "self": {
            "href": "http://localhost:8080/banks/5"
          }
        }
      }

And below is the expected behaviour.

{
        "id": 5,
        "parent_id": 1,
        "children": {},
        "name": "New Child Bank #2",
        "phone": "555-555-5555",
        "fax": "666-666-6666",
        "address1": "55 Any Street",
        "address2": "Suite 100",
        "city": "Wellington",
        "zone_id": 18,
        "zip": "33496",
        "product_1_price": "8.50",
        "product_2_price": "7.80",
        "allow_email_attachment": 1,
        "is_active": 1,
        "created": {
          "date": "2021-03-04 11:23:58.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "modified": {
          "date": "2021-03-04 11:23:58.000000",
          "timezone_type": 3,
          "timezone": "UTC"
        },
        "_links": {
          "self": {
            "href": "http://localhost:8080/banks/5"
          }
        },
        "_embedded": {
          "parent": {
            "id": 1,
            "parent_id": 0,
            "children": {},
            "name": "Bank #1",
            "phone": "555-555-5555",
            "fax": "666-666-6666",
            "address1": "55 Any Street",
            "address2": "Suite 100",
            "city": "Wellington",
            "zone_id": 18,
            "zip": "33496",
            "product_1_price": "0.00",
            "product_2_price": "0.00",
            "allow_email_attachment": 1,
            "is_active": 1,
            "created": {
              "date": "2011-06-04 00:26:43.000000",
              "timezone_type": 3,
              "timezone": "UTC"
            },
            "modified": {
              "date": "2011-12-29 14:19:29.000000",
              "timezone_type": 3,
              "timezone": "UTC"
            },
            "_links": {
              "self": {
                "href": "http://localhost:8080/banks/1"
              }
            }
          }
        }
      }

When the same record is seen on http://localhost:8080/banks?page=1 (by increasing the page size in config.local.php file), it works perfectly fine.

@froschdesign any feedback on this issue?

This might be labelled as Feature Request as the actual behaviour is correct as per Section 4 and 4.1.2 of the rfc draft.

As the extraction of data is done by one single hydrator per resource object i don't see a way to differentiate between "normal data" and embedded data yet.
Extracting all properties which are type of object to embedded will raise another issue, imagine one uses value objects. This would result in every property been moved to embedded.

You could solve your problem by writing a custom ResourceStrategy and altering the data quite a bit:

// snip... see RouteBasedResourceStrategy

$embed = ['parent' => $data['parent']]; 
// prevent data collision
unset($data['parent']);
return new HalResource($data, [
            $resourceGenerator->getLinkGenerator()->fromRoute(
                'self',
                $request,
                $metadata->getRoute(),
                $routeParams
            ),
        ], $embed);

I'd probably do it with a second hydrator instead of this "array hack". Not extracting the parent property with the Extractor Hydrator at all and using the second hydrator which only extracts the the parent