Генерация JSON с вложенными объектами

В БД есть две связанных таблицы. положим, posts и users:

posts
postid
title
post
date
userid

users
userid
username
usermail

Хочется сгенерировать при запросе разом такую структуру, чтобы после преобразования в JSON она приняла следующий вид:

{
postid: 453
title: "The blog post"
post: : "Very important blog post"
date: 2013-08-20
user: {
    userid: 34
    username: "Vasya"
    usermail: "vasya@pupkin.com"
    }
}


Есть серебряная пуля или нужно сначала выбрать посты, а потом дополнительными запросами включать данные о юзерах или иным образом обрабатывать выборку c JOIN?
  • Вопрос задан
  • 7897 просмотров
Решения вопроса 1
Fesor
@Fesor
Full-stack developer (Symfony, Angular)
Вам так или иначе придется привязать к вашей структуре плоские данные. Просто у вас выбор, либо писать это самостоятельно (обычный цикл и мэппинг полей на массив) либо воспользоваться каким-либо orm, который будет заниматься этим за вас.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 7
Sardar
@Sardar
Вас чем-то не устраивает

SELECT ... FROM posts p JOIN users u ON u.userid = p.userid

или, быть может, я не понял вопроса?
Ответ написан
hell0w0rd
@hell0w0rd
Просто разработчик
Нет, так не получится.
Но вы можете такое легко сделать, разбивая названия полей, разделенные точками на подмассивы
SELECT postid, user.userid
FROM posts post
JOIN users user ON user.userid = post.userid
Ответ написан
Комментировать
@Sayonji
Разве что так:
    $q = db_query('SELECT CONCAT(
        "{\"postid\" : \"",
        posts.postid
        "\", \"user\" : {\"username\" : \"",
        users.username,
        "\", \"userid\" : \"",
        users.userid,
        "\"}}"
    ) as res
    FROM posts LEFT JOIN users ON posts.userid = users.userid');
    var_dump(mysql_fetch_all($q));

array(2) {
  [0]=>
  array(1) {
    ["res"]=>
    string(85) "{"postid" : "15", "user" : {"username" : "noobishe", "userid" : "7"}}"
  }
  [1]=>
  array(1) {
    ["res"]=>
    string(79) "{"postid" : "19", "user" : {"username" : "pro!", "userid" : "8"}}"
  }
Ответ написан
Комментировать
ap3rus
@ap3rus
Слишком мало деталей для ответа на вопрос, потому что существует большое множество ответов — например, воспользоваться ORM, скажем, если вы пишете веб-сервис на .NET, можно воспользоваться Entity Framework или LINQ to SQL:

public IList<Post> GetPosts(Guid userId)
{
    using (var context = new DbContext())
    {
        return (from p in context.Posts
            where p.User.Id == userId
            select p).ToList();
    }
}

//...

public class DbContext: DataContext
{
    public Table<Post> Posts { return GetTable<Post>(); }
    public Table<User> Users { return GetTable<User>(); }
    public DbContext(string connectionString):base(connectionString) { }
}

[Table]
public class Post
{
    [Column(IsPrimaryKey = true)]
    public int Id { get; set; }
    [Column]private int _userId;
    private EntityRef<User> _user;
    [Association(Storage = "_user", IsForeignKey = true, ThisKey = "Id")]
    public User User
    {
        get { return _user.Entity; }
        set { _user.Entity = value;_userId = value.Id; }
    }
// ...
}

[Table]
public class User
{
    [Column(IsPrimaryKey = true)]
    public int Id { get; set; }
    
    [Association(OtherKey = "_userId")]private EntitySet<Post> _posts;
// ...
}
Ответ написан
copist
@copist
Empower people to give
В дополнение к Fesor:

Язык вы не указали, поэтому велосипед на PHP

// описание таблиц
$tableMap = array();

// $postsFields и $usersFields - это описание таблиц - в каком точно порядке
//   поля
$tableMap['posts'] = array( // описание таблицы `posts`
	'fields' => array('postid', 'title', 'post', 'date', 'userid'), // перечисление колонок
	'primary' => 'postid', // primary ключ, для упрощения - несоставной
	'relations' => array( // внешние ключи
		'fk_userid' => array('userid', 'users', 'userid'), // ключ на таблицу `users` по полю `userid`
	),
);

$tableMap['users'] = array( // описание таблицы `users`
	'fields' => array('userid', 'username', 'usermail'), // перечисление колонок
	'primary' => 'userid', // primary ключ, для упрощения - несоставной
	'relations' => array( // внешние ключи
	),
);

// $posts и $users - это два хранилища сущностей
$posts = array();
$users = array();

// $resultSet = это имитация результата выборки по запросу
// select
//   p.postid, p.title, p.post, p.date, p.userid,
//   u.userid, u.username, u.usermail
// from posts p
// join users u on p.userid = u.userid
// -- все колонки в этом запросе обязаны следовать в том же порядке, что и в описании таблиц $postsFields и $usersFields

$resulSet = array(
	array(1,'post1_title','post1_content', '2013-10-16', 1, 1, 'login1', 'login1@domain'),
	array(2,'post2_title','post2_content', '2013-10-16', 1, 1, 'login1', 'login1@domain'),
	array(3,'post3_title','post3_content', '2013-10-16', 2, 2, 'login2', 'login2@domain'),
	array(4,'post4_title','post4_content', '2013-10-16', 3, 3, 'login3', 'login3@domain'),
	array(5,'post5_title','post5_content', '2013-10-16', 3, 3, 'login3', 'login3@domain'),
	array(6,'post6_title','post6_content', '2013-10-16', 4, 4, 'login4', 'login4@domain'),
);

// теперь - перебор результатов выборки, составление сущностей с учётом повторения
foreach($resulSet as $row) {
	reset($row);

	// выбор из строки результата всех значений, относящихся к `posts`
	$post = array();
	foreach($tableMap['posts']['fields'] as $key) {
		$post[$key] = current($row);
		next($row);
	}
	// сохранение сущности в хранилище, если ещё не было сохранено
	$pk = $post[$tableMap['posts']['primary']];
	if (!isset($posts[$pk])) {
		$posts[$pk] = $post;
	}

	// выбор из строки результата всех значений, относящихся к `users`
	$user = array();
	foreach($tableMap['users']['fields'] as $key) {
		$user[$key] = current($row);
		next($row);
	}
	// сохранение сущности в хранилище, если ещё не было сохранено
	$pk = $user[$tableMap['users']['primary']];
	if (!isset($users[$pk])) {
		$users[$pk] = $user;
	}
}

// последнее - составление массива с нужной структурой
$collection = array();
$index = 0;
foreach($posts as $post) {
	$collection[$index] = $post;

	// здесь надо воспользоваться $tableMap['posts']['relations']['fk_userid'], но мне лень
	$fkUser = $post['userid'];
	//echo '$fkUser=',$fkUser,PHP_EOL;
	$collection[$index]['user'] = $users[$fkUser];

	$index++;
}

var_export($collection);
Ответ написан
Комментировать
copist
@copist
Empower people to give
А вот так бы выглядело в ORM Propel (PHP)

<?php
$propelQuery = PostsQuery::create()
	->joinWith('users') // join c включением джойненой таблицы в SELECT ...
;
$propelCollection = $propelQuery->find();

$result = array(); $index = 0;
foreach($propelCollection as $post) {
	$result[$index] = $post->toArray();
	$result[$index]['user'] = $post->getUserss()->toArray();
	$index++;
}

var_export($result);
Ответ написан
Комментировать
AMar4enko
@AMar4enko
Если не городить велосипедов, то это обычно делается с помощью ORM. Т.е. вы описываете соответствие классов PHP сущностям БД, описываете взаимосвязи между сущностями, потом говорите «Выбрать мне posts вместе с user». В ответ получаете уже готовые объекты класса Post, у которых, например, свойство user также является объектом класса User. После всего этого вам останется только сделать json_encode ну или с препроцессингом каким-то, по ситуации.
А если без ORM, то единственный выход это самостоятельно связывать результаты запросов из таблиц. В этом случае может помочь yalinqo, но по опыту лучше вам по ORM-path.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы