1
0
mirror of https://github.com/flarum/core.git synced 2025-08-10 10:24:46 +02:00

Load discussion and posts with one request

Speeds things up a heap.
Also fix a whole bunch of bugs with the post stream.
This commit is contained in:
Toby Zerner
2015-02-06 10:30:38 +10:30
parent bc3aa449e7
commit 0365ae6c71
13 changed files with 279 additions and 164 deletions

View File

@@ -26,7 +26,7 @@ abstract class Base extends Controller
abstract protected function run();
public function handle($request, $parameters)
public function handle($request, $parameters = [])
{
$this->registerErrorHandlers();
@@ -39,6 +39,11 @@ abstract class Base extends Controller
return $this->run();
}
public function setRequest($request)
{
$this->request = $request;
}
public function param($key, $default = null)
{
return array_get($this->parameters, $key, $default);
@@ -74,7 +79,7 @@ abstract class Base extends Controller
protected function included($available)
{
$requested = explode(',', $this->input('include'));
return array_intersect($available, $requested);
return array_intersect((array) $available, $requested);
}
protected function explodeIds($ids)

View File

@@ -1,11 +1,32 @@
<?php namespace Flarum\Api\Actions\Discussions;
use Flarum\Core\Discussions\Discussion;
use Flarum\Core\Posts\PostRepository;
use Flarum\Api\Actions\Base;
use Flarum\Api\Actions\Posts\GetsPostsForDiscussion;
use Flarum\Api\Serializers\DiscussionSerializer;
class Show extends Base
{
use GetsPostsForDiscussion;
/**
* The post repository.
*
* @var PostRepository
*/
protected $posts;
/**
* Instantiate the action.
*
* @param PostRepository $posts
*/
public function __construct(PostRepository $posts)
{
$this->posts = $posts;
}
/**
* Show a single discussion.
*
@@ -13,10 +34,19 @@ class Show extends Base
*/
protected function run()
{
$include = $this->included(['startPost', 'lastPost']);
$include = $this->included(['startPost', 'lastPost', 'posts']);
$discussion = Discussion::whereCanView()->findOrFail($this->param('id'));
if (in_array('posts', $include)) {
$relations = ['user', 'user.groups', 'editUser', 'deleteUser'];
$discussion->posts = $this->getPostsForDiscussion($this->posts, $discussion->id, $relations);
$include = array_merge($include, array_map(function ($relation) {
return 'posts.'.$relation;
}, $relations));
}
// Set up the discussion serializer, which we will use to create the
// document's primary resource. As well as including the requested
// relations, we will specify that we want the 'posts' relation to be

View File

@@ -0,0 +1,19 @@
<?php namespace Flarum\Api\Actions\Posts;
trait GetsPostsForDiscussion
{
protected function getPostsForDiscussion($repository, $discussionId, $relations = [])
{
$sort = $this->sort(['time']);
$count = $this->count(20, 50);
if (($near = $this->input('near')) > 1) {
$start = $repository->getIndexForNumber($discussionId, $near);
$start = max(0, $start - $count / 2);
} else {
$start = 0;
}
return $repository->findByDiscussion($discussionId, $relations, $sort['by'], $sort['order'] ?: 'asc', $count, $start);
}
}

View File

@@ -2,11 +2,31 @@
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Flarum\Core\Posts\Post;
use Flarum\Core\Posts\PostRepository;
use Flarum\Api\Actions\Base;
use Flarum\Api\Serializers\PostSerializer;
class Index extends Base
{
use GetsPostsForDiscussion;
/**
* The post repository.
*
* @var PostRepository
*/
protected $posts;
/**
* Instantiate the action.
*
* @param PostRepository $posts
*/
public function __construct(PostRepository $posts)
{
$this->posts = $posts;
}
/**
* Show posts from a discussion, or by providing an array of IDs.
*
@@ -14,47 +34,16 @@ class Index extends Base
*/
protected function run()
{
$discussionId = $this->input('discussions');
$postIds = (array) $this->input('ids');
$include = ['user', 'user.groups', 'editUser', 'deleteUser'];
$count = $this->count(20, 50);
if ($near = $this->input('near')) {
// fetch the nearest post
$post = Post::orderByRaw('ABS(CAST(number AS SIGNED) - ?)', [$near])->whereNotNull('number')->where('discussion_id', $discussionId)->take(1)->first();
$start = max(
0,
Post::whereCanView()
->where('discussion_id', $discussionId)
->where('time', '<=', $post->time)
->count() - round($count / 2)
);
} else {
$start = $this->start();
}
$include = $this->included([]);
$sort = $this->sort(['time']);
$relations = array_merge(['user', 'user.groups', 'editUser', 'deleteUser'], $include);
// @todo move to post repository
$posts = Post::with($relations)
->whereCanView();
if ($discussionId) {
$posts->where('discussion_id', $discussionId);
}
if (count($postIds)) {
$posts->whereIn('id', $postIds);
$posts = $this->posts->findMany($postIds, $include);
} else {
$discussionId = $this->input('discussions');
$posts = $this->getPostsForDiscussion($this->posts, $discussionId, $include);
}
$posts = $posts->skip($start)
->take($count)
->orderBy($sort['by'], $sort['order'] ?: 'asc')
->get();
if (! count($posts)) {
throw new ModelNotFoundException;
}
@@ -62,7 +51,7 @@ class Index extends Base
// Finally, we can set up the post serializer and use it to create
// a post resource or collection, depending on how many posts were
// requested.
$serializer = new PostSerializer($relations);
$serializer = new PostSerializer($include);
$this->document->setPrimaryElement($serializer->collection($posts));
return $this->respondWithDocument();

View File

@@ -58,7 +58,8 @@ class DiscussionSerializer extends DiscussionBasicSerializer
}
/**
* Get a collection containing a discussion's viewable posts.
* Get a collection containing a discussion's viewable posts. Assumes that
* the discussion model's posts attributes has been filled.
*
* @param Discussion $discussion
* @param array $relations
@@ -66,7 +67,7 @@ class DiscussionSerializer extends DiscussionBasicSerializer
*/
public function includePosts(Discussion $discussion, $relations)
{
return (new PostSerializer($relations))->collection($discussion->posts()->whereCanView()->get());
return (new PostSerializer($relations))->collection($discussion->posts);
}
/**

View File

@@ -18,6 +18,40 @@ class PostRepository
return $query->findOrFail($id);
}
public function getIndexForNumber($discussionId, $number)
{
return Post::whereCanView()
->where('discussion_id', $discussionId)
->where('time', '<', function ($query) use ($discussionId, $number) {
$query->select('time')
->from('posts')
->where('discussion_id', $discussionId)
->whereNotNull('number')
->orderByRaw('ABS(CAST(number AS SIGNED) - ?)', [$number])
->take(1);
})
->count();
}
public function findByDiscussion($discussionId, $relations = [], $sortBy = 'time', $sortOrder = 'asc', $count = null, $start = 0)
{
return Post::with($relations)
->whereCanView()
->where('discussion_id', $discussionId)
->skip($start)
->take($count)
->orderBy($sortBy, $sortOrder)
->get();
}
public function findMany($ids, $relations = [])
{
return Post::with($relations)
->whereCanView()
->whereIn('id', $ids)
->get();
}
public function save(Post $post)
{
$post->assertValid();