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:
@@ -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)
|
||||
|
@@ -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
|
||||
|
19
src/Flarum/Api/Actions/Posts/GetsPostsForDiscussion.php
Normal file
19
src/Flarum/Api/Actions/Posts/GetsPostsForDiscussion.php
Normal 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);
|
||||
}
|
||||
}
|
@@ -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();
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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();
|
||||
|
Reference in New Issue
Block a user