1
0
mirror of https://github.com/flarum/core.git synced 2025-08-06 00:17:31 +02:00

Get rid of Repository interfaces

This commit is contained in:
Toby Zerner
2015-07-04 18:38:59 +09:30
parent f7b6d8a568
commit 86811c6508
43 changed files with 203 additions and 341 deletions

View File

@@ -0,0 +1,181 @@
<?php namespace Flarum\Core\Posts;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Flarum\Core\Users\User;
use Flarum\Core\Discussions\Discussion;
use Flarum\Core\Discussions\Search\Fulltext\DriverInterface;
// TODO: In some cases, the use of a post repository incurs extra query expense,
// because for every post retrieved we need to check if the discussion it's in
// is visible. Especially when retrieving a discussion's posts, we can end up
// with an inefficient chain of queries like this:
// 1. Api\Discussions\ShowAction: get discussion (will exit if not visible)
// 2. Discussion@postsVisibleTo: get discussion tags (for post visibility purposes)
// 3. Discussion@postsVisibleTo: get post IDs
// 4. EloquentPostRepository@getIndexForNumber: get discussion
// 5. EloquentPostRepository@getIndexForNumber: get discussion tags (for post visibility purposes)
// 6. EloquentPostRepository@getIndexForNumber: get post index for number
// 7. EloquentPostRepository@findWhere: get post IDs for discussion to check for discussion visibility
// 8. EloquentPostRepository@findWhere: get post IDs in visible discussions
// 9. EloquentPostRepository@findWhere: get posts
// 10. EloquentPostRepository@findWhere: eager load discussion onto posts
// 11. EloquentPostRepository@findWhere: get discussion tags to filter visible posts
// 12. Api\Discussions\ShowAction: eager load users
// 13. Api\Discussions\ShowAction: eager load groups
// 14. Api\Discussions\ShowAction: eager load mentions
// 14. Serializers\DiscussionSerializer: load discussion-user state
class PostRepository
{
protected $fulltext;
public function __construct(DriverInterface $fulltext)
{
$this->fulltext = $fulltext;
}
/**
* Find a post by ID, optionally making sure it is visible to a certain
* user, or throw an exception.
*
* @param integer $id
* @param \Flarum\Core\Users\User $actor
* @return \Flarum\Core\Posts\Post
*
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
*/
public function findOrFail($id, User $actor = null)
{
$posts = $this->findByIds([$id], $actor);
if (! count($posts)) {
throw new ModelNotFoundException;
}
return $posts->first();
}
/**
* Find posts that match certain conditions, optionally making sure they
* are visible to a certain user, and/or using other criteria.
*
* @param array $where
* @param \Flarum\Core\Users\User|null $actor
* @param array $sort
* @param integer $count
* @param integer $start
* @return \Illuminate\Database\Eloquent\Collection
*/
public function findWhere($where = [], User $actor = null, $sort = [], $count = null, $start = 0)
{
$query = Post::where($where)
->skip($start)
->take($count);
foreach ((array) $sort as $field => $order) {
$query->orderBy($field, $order);
}
$ids = $query->lists('id');
return $this->findByIds($ids, $actor);
}
/**
* Find posts by their IDs, optionally making sure they are visible to a
* certain user.
*
* @param array $ids
* @param \Flarum\Core\Users\User|null $actor
* @return \Illuminate\Database\Eloquent\Collection
*/
public function findByIds(array $ids, User $actor = null)
{
$ids = $this->filterDiscussionVisibleTo($ids, $actor);
$posts = Post::with('discussion')->whereIn('id', (array) $ids)->get();
return $this->filterVisibleTo($posts, $actor);
}
/**
* Find posts by matching a string of words against their content,
* optionally making sure they are visible to a certain user.
*
* @param string $string
* @param \Flarum\Core\Users\User|null $actor
* @return \Illuminate\Database\Eloquent\Collection
*/
public function findByContent($string, User $actor = null)
{
$ids = $this->fulltext->match($string);
$ids = $this->filterDiscussionVisibleTo($ids, $actor);
$query = Post::select('id', 'discussion_id')->whereIn('id', $ids);
foreach ($ids as $id) {
$query->orderByRaw('id != ?', [$id]);
}
$posts = $query->get();
return $this->filterVisibleTo($posts, $actor);
}
/**
* Get the position within a discussion where a post with a certain number
* is. If the post with that number does not exist, the index of the
* closest post to it will be returned.
*
* @param integer $discussionId
* @param integer $number
* @param \Flarum\Core\Users\User|null $actor
* @return integer
*/
public function getIndexForNumber($discussionId, $number, User $actor = null)
{
$query = Discussion::find($discussionId)
->postsVisibleTo($actor)
->where('time', '<', function ($query) use ($discussionId, $number) {
$query->select('time')
->from('posts')
->where('discussion_id', $discussionId)
->whereNotNull('number')
->take(1)
// We don't add $number as a binding because for some
// reason doing so makes the bindings go out of order.
->orderByRaw('ABS(CAST(number AS SIGNED) - '.(int) $number.')');
});
return $query->count();
}
protected function filterDiscussionVisibleTo($ids, User $actor)
{
// For each post ID, we need to make sure that the discussion it's in
// is visible to the user.
if ($actor) {
$ids = Discussion::join('posts', 'discussions.id', '=', 'posts.discussion_id')
->whereIn('posts.id', $ids)
->whereVisibleTo($actor)
->get(['posts.id'])
->lists('id');
}
return $ids;
}
protected function filterVisibleTo($posts, User $actor)
{
if ($actor) {
$posts = $posts->filter(function ($post) use ($actor) {
return $post->can($actor, 'view');
});
}
return $posts;
}
}