WITH RECURSIVE `cte` (`id`, `parent_id`, `title`, `n`) AS (
  SELECT `id`, `parent_id`, `title`, 0
    FROM `table`
    WHERE `id` = :categoryId
  UNION
  SELECT `t`.`id`, `t`.`parent_id`, `t`.`title`, `n`+1
    FROM `cte`
    JOIN `table` AS `t` ON `t`.`id` = `cte`.`parent_id`
)
SELECT `id`, `title`
  FROM `cte`
  ORDER BY `n` DESCОстаётся только выбрать все строки из ответа и вывести их в цикле.      $data = [[
    "id" => 25,
    "pid" => "0CjqYfx3NU1AghsPVoGFullXwl4umV",
    "message" => "Одно лишь ё - это безумие!"
]];
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
/*
[
    {
        "id": 25,
        "pid": "0CjqYfx3NU1AghsPVoGFullXwl4umV",
        "message": "Одно лишь ё - это безумие!"
    }
]
*/      const has = (object, key) => (key in object);
const gendiff = (file1, file2) => {
  const mergeFiles = { ...file1, ...file2 };
  const uniqueKeys = Object.keys(mergeFiles);
  const diff = uniqueKeys.map(key => {
    if (has(file1, key) && has(file2, key) && typeof file1[key] === 'object' && typeof file2[key] === 'object') {
      return { key, value: gendiff(file1[key], file2[key]), status: 'have a child' };
    } else if (has(file1, key) && has(file2, key) && file1[key] !== file2[key]) {
      return { key, value: { oldValue: file1[key], newValue: file2[key] }, status: 'changed' };
    } else if (has(file1, key) && !has(file2, key)) {
      return { key, value: file1[key], status: 'removed' };
    } else if (!has(file1, key) && has(file2, key)) {
      return { key, value: file2[key], status: 'new' };
    } else if (has(file1, key) && has(file2, key) && file1[key] === file2[key]) {
      return { key, value: file1[key], status: 'same' };
    }
  });
  
  return diff;
};console.log(gendiff(
  { workshop: { data: '2.0000000', locales: 'zh-CN.pak' } },
  { workshop: { config: 'd7', locales: 'am.pak' } }
));
/*
[
  {
    key: "workshop",
    status: "have a child",
    value: [
      { key: "data", value: "2.0000000", status: "removed" },
      {
        key: "locales",
        status: "changed",
        value: { oldValue: "zh-CN.pak", newValue: "am.pak" },
      },
      { key: "config", value: "d7", status: "new" },
    ],
  },
]
*/