Added post favoriting
This commit is contained in:
parent
6d4a4f11d1
commit
6e22efdd6e
21 changed files with 636 additions and 86 deletions
3
TODO
3
TODO
|
@ -3,7 +3,6 @@ first major release.
|
||||||
|
|
||||||
everything related to posts:
|
everything related to posts:
|
||||||
- single post view
|
- single post view
|
||||||
- fav
|
|
||||||
- score
|
- score
|
||||||
- editing
|
- editing
|
||||||
- ability to loop video posts
|
- ability to loop video posts
|
||||||
|
@ -104,8 +103,6 @@ refactors:
|
||||||
with query decorated by PostDao::decorateQueryFromFilter, call
|
with query decorated by PostDao::decorateQueryFromFilter, call
|
||||||
PostDao::decorateEntitiesWithFilter that will optimally load users and
|
PostDao::decorateEntitiesWithFilter that will optimally load users and
|
||||||
inject them using posts' lazy loaders.
|
inject them using posts' lazy loaders.
|
||||||
- make view proxies less greedy, e.g. favs should be fetched only in post
|
|
||||||
view, not for each of 40 posts in post list. (same goes to other things)
|
|
||||||
- reduce dependencies
|
- reduce dependencies
|
||||||
|
|
||||||
miscellaneous:
|
miscellaneous:
|
||||||
|
|
|
@ -56,6 +56,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#post-view-wrapper .favorites li {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 0.25em 0.25em 0;
|
||||||
|
}
|
||||||
|
|
||||||
#sidebar ul {
|
#sidebar ul {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
|
|
|
@ -22,6 +22,7 @@ App.Presenters.PostPresenter = function(
|
||||||
var historyTemplate;
|
var historyTemplate;
|
||||||
|
|
||||||
var post;
|
var post;
|
||||||
|
var postFavorites;
|
||||||
var postHistory;
|
var postHistory;
|
||||||
var postNameOrId;
|
var postNameOrId;
|
||||||
|
|
||||||
|
@ -74,11 +75,16 @@ App.Presenters.PostPresenter = function(
|
||||||
|
|
||||||
promise.waitAll(
|
promise.waitAll(
|
||||||
api.get('/posts/' + postNameOrId),
|
api.get('/posts/' + postNameOrId),
|
||||||
|
api.get('/posts/' + postNameOrId + '/favorites'),
|
||||||
privileges.canViewHistory ?
|
privileges.canViewHistory ?
|
||||||
api.get('/posts/' + postNameOrId + '/history') :
|
api.get('/posts/' + postNameOrId + '/history') :
|
||||||
null)
|
null)
|
||||||
.then(function(postResponse, postHistoryResponse) {
|
.then(function(
|
||||||
|
postResponse,
|
||||||
|
postFavoritesResponse,
|
||||||
|
postHistoryResponse) {
|
||||||
post = postResponse.json;
|
post = postResponse.json;
|
||||||
|
postFavorites = postFavoritesResponse && postFavoritesResponse.json && postFavoritesResponse.json.data;
|
||||||
postHistory = postHistoryResponse && postHistoryResponse.json && postHistoryResponse.json.data;
|
postHistory = postHistoryResponse && postHistoryResponse.json && postHistoryResponse.json.data;
|
||||||
topNavigationPresenter.changeTitle('@' + post.id);
|
topNavigationPresenter.changeTitle('@' + post.id);
|
||||||
render();
|
render();
|
||||||
|
@ -112,30 +118,43 @@ App.Presenters.PostPresenter = function(
|
||||||
}
|
}
|
||||||
|
|
||||||
$el.find('.post-edit-wrapper form').submit(editFormSubmitted);
|
$el.find('.post-edit-wrapper form').submit(editFormSubmitted);
|
||||||
$el.find('#sidebar .delete').click(deleteButtonClicked);
|
attachSidebarEvents();
|
||||||
$el.find('#sidebar .feature').click(featureButtonClicked);
|
|
||||||
$el.find('#sidebar .edit').click(editButtonClicked);
|
|
||||||
$el.find('#sidebar .history').click(historyButtonClicked);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderSidebar() {
|
function renderSidebar() {
|
||||||
$el.find('#sidebar').html(jQuery(renderPostTemplate()).find('#sidebar').html());
|
$el.find('#sidebar').html(jQuery(renderPostTemplate()).find('#sidebar').html());
|
||||||
|
attachSidebarEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPostTemplate() {
|
function renderPostTemplate() {
|
||||||
return postTemplate({
|
return postTemplate({
|
||||||
post: post,
|
post: post,
|
||||||
|
postFavorites: postFavorites,
|
||||||
postHistory: postHistory,
|
postHistory: postHistory,
|
||||||
|
|
||||||
formatRelativeTime: util.formatRelativeTime,
|
formatRelativeTime: util.formatRelativeTime,
|
||||||
formatFileSize: util.formatFileSize,
|
formatFileSize: util.formatFileSize,
|
||||||
|
|
||||||
postContentTemplate: postContentTemplate,
|
postContentTemplate: postContentTemplate,
|
||||||
postEditTemplate: postEditTemplate,
|
postEditTemplate: postEditTemplate,
|
||||||
historyTemplate: historyTemplate,
|
historyTemplate: historyTemplate,
|
||||||
|
|
||||||
|
hasFav: _.any(postFavorites, function(favUser) { return favUser.id === auth.getCurrentUser().id; }),
|
||||||
|
isLoggedIn: auth.isLoggedIn(),
|
||||||
privileges: privileges,
|
privileges: privileges,
|
||||||
editPrivileges: editPrivileges,
|
editPrivileges: editPrivileges,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function attachSidebarEvents() {
|
||||||
|
$el.find('#sidebar .delete').click(deleteButtonClicked);
|
||||||
|
$el.find('#sidebar .feature').click(featureButtonClicked);
|
||||||
|
$el.find('#sidebar .edit').click(editButtonClicked);
|
||||||
|
$el.find('#sidebar .history').click(historyButtonClicked);
|
||||||
|
$el.find('#sidebar .add-favorite').click(addFavoriteButtonClicked);
|
||||||
|
$el.find('#sidebar .delete-favorite').click(deleteFavoriteButtonClicked);
|
||||||
|
}
|
||||||
|
|
||||||
function deleteButtonClicked(e) {
|
function deleteButtonClicked(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
messagePresenter.hideMessages($messages);
|
messagePresenter.hideMessages($messages);
|
||||||
|
@ -268,6 +287,34 @@ App.Presenters.PostPresenter = function(
|
||||||
$el.find('.post-history-wrapper').slideDown('slow');
|
$el.find('.post-history-wrapper').slideDown('slow');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addFavoriteButtonClicked(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
addFavorite();
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteFavoriteButtonClicked(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
deleteFavorite();
|
||||||
|
}
|
||||||
|
|
||||||
|
function addFavorite() {
|
||||||
|
api.post('/posts/' + post.id + '/favorites')
|
||||||
|
.then(function(response) {
|
||||||
|
postFavorites = response.json.data;
|
||||||
|
renderSidebar();
|
||||||
|
})
|
||||||
|
.fail(showGenericError);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteFavorite() {
|
||||||
|
api.delete('/posts/' + post.id + '/favorites')
|
||||||
|
.then(function(response) {
|
||||||
|
postFavorites = response.json.data;
|
||||||
|
renderSidebar();
|
||||||
|
})
|
||||||
|
.fail(showGenericError);
|
||||||
|
}
|
||||||
|
|
||||||
function showEditError(response) {
|
function showEditError(response) {
|
||||||
window.alert(response.json && response.json.error || response);
|
window.alert(response.json && response.json.error || response);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,20 @@
|
||||||
<%= post.contentExtension + ', ' + formatFileSize(post.originalFileSize) %>
|
<%= post.contentExtension + ', ' + formatFileSize(post.originalFileSize) %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<% if (isLoggedIn) { %>
|
||||||
|
<li>
|
||||||
|
<% if (hasFav) { %>
|
||||||
|
<a href="#" class="delete-favorite">
|
||||||
|
<i class="fa fa-heart"></i>
|
||||||
|
</a>
|
||||||
|
<% } else { %>
|
||||||
|
<a href="#" class="add-favorite">
|
||||||
|
<i class="fa fa-heart-o"></i>
|
||||||
|
</a>
|
||||||
|
<% } %>
|
||||||
|
</li>
|
||||||
|
<% } %>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h1>Tags (<%= _.size(post.tags) %>)</h1>
|
<h1>Tags (<%= _.size(post.tags) %>)</h1>
|
||||||
|
@ -98,9 +112,24 @@
|
||||||
--><% } %><!--
|
--><% } %><!--
|
||||||
--></li>
|
--></li>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<% if (_.any(postFavorites)) { %>
|
||||||
|
<p>Favorites:</p>
|
||||||
|
|
||||||
|
<ul class="favorites">
|
||||||
|
<% _.each(postFavorites, function(user) { %>
|
||||||
|
<li>
|
||||||
|
<a href="#/user/<%= user.name %>">
|
||||||
|
<img class="fav-avatar"
|
||||||
|
src="/data/thumbnails/25x25/avatars/<%= user.name || '!' %>"
|
||||||
|
alt="<%= user.name || 'Anonymous user' %>"/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<% }) %>
|
||||||
|
</ul>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
<% if (_.any(post.relations)) { %>
|
<% if (_.any(post.relations)) { %>
|
||||||
<h1>Related posts</h1>
|
<h1>Related posts</h1>
|
||||||
<ul class="related">
|
<ul class="related">
|
||||||
|
|
57
src/Controllers/FavoritesController.php
Normal file
57
src/Controllers/FavoritesController.php
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
namespace Szurubooru\Controllers;
|
||||||
|
|
||||||
|
class FavoritesController extends AbstractController
|
||||||
|
{
|
||||||
|
private $privilegeService;
|
||||||
|
private $authService;
|
||||||
|
private $postService;
|
||||||
|
private $favoritesService;
|
||||||
|
private $userViewProxy;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
\Szurubooru\Services\PrivilegeService $privilegeService,
|
||||||
|
\Szurubooru\Services\AuthService $authService,
|
||||||
|
\Szurubooru\Services\PostService $postService,
|
||||||
|
\Szurubooru\Services\FavoritesService $favoritesService,
|
||||||
|
\Szurubooru\Controllers\ViewProxies\UserViewProxy $userViewProxy)
|
||||||
|
{
|
||||||
|
$this->privilegeService = $privilegeService;
|
||||||
|
$this->authService = $authService;
|
||||||
|
$this->postService = $postService;
|
||||||
|
$this->favoritesService = $favoritesService;
|
||||||
|
$this->userViewProxy = $userViewProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerRoutes(\Szurubooru\Router $router)
|
||||||
|
{
|
||||||
|
$router->get('/api/posts/:postNameOrId/favorites', [$this, 'getFavoriteUsers']);
|
||||||
|
$router->post('/api/posts/:postNameOrId/favorites', [$this, 'addFavorite']);
|
||||||
|
$router->delete('/api/posts/:postNameOrId/favorites', [$this, 'deleteFavorite']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFavoriteUsers($postNameOrId)
|
||||||
|
{
|
||||||
|
$post = $this->postService->getByNameOrId($postNameOrId);
|
||||||
|
$users = $this->favoritesService->getFavoriteUsers($post);
|
||||||
|
return ['data' => $this->userViewProxy->fromArray($users)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addFavorite($postNameOrId)
|
||||||
|
{
|
||||||
|
$this->privilegeService->assertLoggedIn();
|
||||||
|
$user = $this->authService->getLoggedInUser();
|
||||||
|
$post = $this->postService->getByNameOrId($postNameOrId);
|
||||||
|
$this->favoritesService->addFavorite($user, $post);
|
||||||
|
return $this->getFavoriteUsers($postNameOrId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteFavorite($postNameOrId)
|
||||||
|
{
|
||||||
|
$this->privilegeService->assertLoggedIn();
|
||||||
|
$user = $this->authService->getLoggedInUser();
|
||||||
|
$post = $this->postService->getByNameOrId($postNameOrId);
|
||||||
|
$this->favoritesService->deleteFavorite($user, $post);
|
||||||
|
return $this->getFavoriteUsers($postNameOrId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -133,14 +133,9 @@ abstract class AbstractDao implements ICrudDao
|
||||||
|
|
||||||
protected function findBy($columnName, $value)
|
protected function findBy($columnName, $value)
|
||||||
{
|
{
|
||||||
$entities = [];
|
|
||||||
$query = $this->fpdo->from($this->tableName)->where($columnName, $value);
|
$query = $this->fpdo->from($this->tableName)->where($columnName, $value);
|
||||||
foreach ($query as $arrayEntity)
|
$arrayEntities = iterator_to_array($query);
|
||||||
{
|
return $this->arrayToEntities($arrayEntities);
|
||||||
$entity = $this->entityConverter->toEntity($arrayEntity);
|
|
||||||
$entities[$entity->getId()] = $entity;
|
|
||||||
}
|
|
||||||
return $entities;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function findOneBy($columnName, $value)
|
protected function findOneBy($columnName, $value)
|
||||||
|
|
23
src/Dao/EntityConverters/FavoriteEntityConverter.php
Normal file
23
src/Dao/EntityConverters/FavoriteEntityConverter.php
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
namespace Szurubooru\Dao\EntityConverters;
|
||||||
|
|
||||||
|
class FavoriteEntityConverter extends AbstractEntityConverter implements IEntityConverter
|
||||||
|
{
|
||||||
|
public function toArray(\Szurubooru\Entities\Entity $entity)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
[
|
||||||
|
'id' => $entity->getId(),
|
||||||
|
'userId' => $entity->getUserId(),
|
||||||
|
'postId' => $entity->getPostId(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toBasicEntity(array $array)
|
||||||
|
{
|
||||||
|
$entity = new \Szurubooru\Entities\Favorite($array['id']);
|
||||||
|
$entity->setUserId($array['userId']);
|
||||||
|
$entity->setPostId($array['postId']);
|
||||||
|
return $entity;
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,6 +45,7 @@ class PostEntityConverter extends AbstractEntityConverter implements IEntityConv
|
||||||
$entity->setFeatureCount(intval($array['featureCount']));
|
$entity->setFeatureCount(intval($array['featureCount']));
|
||||||
$entity->setLastFeatureTime($array['lastFeatureTime']);
|
$entity->setLastFeatureTime($array['lastFeatureTime']);
|
||||||
$entity->setMeta(\Szurubooru\Entities\Post::META_TAG_COUNT, intval($array['tagCount']));
|
$entity->setMeta(\Szurubooru\Entities\Post::META_TAG_COUNT, intval($array['tagCount']));
|
||||||
|
$entity->setMeta(\Szurubooru\Entities\Post::META_FAV_COUNT, intval($array['favCount']));
|
||||||
return $entity;
|
return $entity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
54
src/Dao/FavoritesDao.php
Normal file
54
src/Dao/FavoritesDao.php
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
namespace Szurubooru\Dao;
|
||||||
|
|
||||||
|
class FavoritesDao extends AbstractDao implements ICrudDao
|
||||||
|
{
|
||||||
|
private $userDao;
|
||||||
|
private $postDao;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
\Szurubooru\DatabaseConnection $databaseConnection,
|
||||||
|
\Szurubooru\Dao\UserDao $userDao,
|
||||||
|
\Szurubooru\Dao\PostDao $postDao)
|
||||||
|
{
|
||||||
|
parent::__construct(
|
||||||
|
$databaseConnection,
|
||||||
|
'favorites',
|
||||||
|
new \Szurubooru\Dao\EntityConverters\FavoriteEntityConverter());
|
||||||
|
|
||||||
|
$this->userDao = $userDao;
|
||||||
|
$this->postDao = $postDao;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findByUserAndPost(\Szurubooru\Entities\User $user, \Szurubooru\Entities\Post $post)
|
||||||
|
{
|
||||||
|
$query = $this->fpdo->from($this->tableName)
|
||||||
|
->where('userId', $user->getId())
|
||||||
|
->where('postId', $post->getId());
|
||||||
|
$arrayEntities = iterator_to_array($query);
|
||||||
|
$entities = $this->arrayToEntities($arrayEntities);
|
||||||
|
return array_shift($entities);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findByPost(\Szurubooru\Entities\Post $post)
|
||||||
|
{
|
||||||
|
return $this->findBy('postId', $post->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function afterLoad(\Szurubooru\Entities\Entity $favorite)
|
||||||
|
{
|
||||||
|
$favorite->setLazyLoader(
|
||||||
|
\Szurubooru\Entities\Favorite::LAZY_LOADER_USER,
|
||||||
|
function (\Szurubooru\Entities\Favorite $favorite)
|
||||||
|
{
|
||||||
|
return $this->userDao->findById($favorite->getUserId());
|
||||||
|
});
|
||||||
|
|
||||||
|
$favorite->setLazyLoader(
|
||||||
|
\Szurubooru\Entities\Favorite::LAZY_LOADER_POST,
|
||||||
|
function (\Szurubooru\Entities\Favorite $favorite)
|
||||||
|
{
|
||||||
|
return $this->postDao->findById($favorite->getPostId());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -51,35 +51,35 @@ class PostDao extends AbstractDao implements ICrudDao
|
||||||
{
|
{
|
||||||
$post->setLazyLoader(
|
$post->setLazyLoader(
|
||||||
\Szurubooru\Entities\Post::LAZY_LOADER_CONTENT,
|
\Szurubooru\Entities\Post::LAZY_LOADER_CONTENT,
|
||||||
function(\Szurubooru\Entities\Post $post)
|
function (\Szurubooru\Entities\Post $post)
|
||||||
{
|
{
|
||||||
return $this->fileService->load($post->getContentPath());
|
return $this->fileService->load($post->getContentPath());
|
||||||
});
|
});
|
||||||
|
|
||||||
$post->setLazyLoader(
|
$post->setLazyLoader(
|
||||||
\Szurubooru\Entities\Post::LAZY_LOADER_THUMBNAIL_SOURCE_CONTENT,
|
\Szurubooru\Entities\Post::LAZY_LOADER_THUMBNAIL_SOURCE_CONTENT,
|
||||||
function(\Szurubooru\Entities\Post $post)
|
function (\Szurubooru\Entities\Post $post)
|
||||||
{
|
{
|
||||||
return $this->fileService->load($post->getThumbnailSourceContentPath());
|
return $this->fileService->load($post->getThumbnailSourceContentPath());
|
||||||
});
|
});
|
||||||
|
|
||||||
$post->setLazyLoader(
|
$post->setLazyLoader(
|
||||||
\Szurubooru\Entities\Post::LAZY_LOADER_USER,
|
\Szurubooru\Entities\Post::LAZY_LOADER_USER,
|
||||||
function(\Szurubooru\Entities\Post $post)
|
function (\Szurubooru\Entities\Post $post)
|
||||||
{
|
{
|
||||||
return $this->getUser($post);
|
return $this->getUser($post);
|
||||||
});
|
});
|
||||||
|
|
||||||
$post->setLazyLoader(
|
$post->setLazyLoader(
|
||||||
\Szurubooru\Entities\Post::LAZY_LOADER_TAGS,
|
\Szurubooru\Entities\Post::LAZY_LOADER_TAGS,
|
||||||
function(\Szurubooru\Entities\Post $post)
|
function (\Szurubooru\Entities\Post $post)
|
||||||
{
|
{
|
||||||
return $this->getTags($post);
|
return $this->getTags($post);
|
||||||
});
|
});
|
||||||
|
|
||||||
$post->setLazyLoader(
|
$post->setLazyLoader(
|
||||||
\Szurubooru\Entities\Post::LAZY_LOADER_RELATED_POSTS,
|
\Szurubooru\Entities\Post::LAZY_LOADER_RELATED_POSTS,
|
||||||
function(\Szurubooru\Entities\Post $post)
|
function (\Szurubooru\Entities\Post $post)
|
||||||
{
|
{
|
||||||
return $this->getRelatedPosts($post);
|
return $this->getRelatedPosts($post);
|
||||||
});
|
});
|
||||||
|
@ -179,14 +179,14 @@ class PostDao extends AbstractDao implements ICrudDao
|
||||||
$this->tagDao->createMissingTags($tagNames);
|
$this->tagDao->createMissingTags($tagNames);
|
||||||
|
|
||||||
$tagIds = array_map(
|
$tagIds = array_map(
|
||||||
function($tag)
|
function ($tag)
|
||||||
{
|
{
|
||||||
return $tag->getId();
|
return $tag->getId();
|
||||||
},
|
},
|
||||||
$this->tagDao->findByNames($tagNames));
|
$this->tagDao->findByNames($tagNames));
|
||||||
|
|
||||||
$existingTagRelationIds = array_map(
|
$existingTagRelationIds = array_map(
|
||||||
function($arrayEntity)
|
function ($arrayEntity)
|
||||||
{
|
{
|
||||||
return $arrayEntity['tagId'];
|
return $arrayEntity['tagId'];
|
||||||
},
|
},
|
||||||
|
|
|
@ -43,6 +43,7 @@ abstract class Entity
|
||||||
public function resetLazyLoaders()
|
public function resetLazyLoaders()
|
||||||
{
|
{
|
||||||
$this->lazyLoaders = [];
|
$this->lazyLoaders = [];
|
||||||
|
$this->lazyContainers = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setLazyLoader($lazyContainerName, $getter)
|
public function setLazyLoader($lazyContainerName, $getter)
|
||||||
|
|
53
src/Entities/Favorite.php
Normal file
53
src/Entities/Favorite.php
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
namespace Szurubooru\Entities;
|
||||||
|
|
||||||
|
class Favorite extends Entity
|
||||||
|
{
|
||||||
|
private $postId;
|
||||||
|
private $userId;
|
||||||
|
|
||||||
|
const LAZY_LOADER_USER = 'user';
|
||||||
|
const LAZY_LOADER_POST = 'post';
|
||||||
|
|
||||||
|
public function getUserId()
|
||||||
|
{
|
||||||
|
return $this->userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUserId($userId)
|
||||||
|
{
|
||||||
|
$this->userId = $userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPostId()
|
||||||
|
{
|
||||||
|
return $this->postId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPostId($postId)
|
||||||
|
{
|
||||||
|
$this->postId = $postId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUser()
|
||||||
|
{
|
||||||
|
return $this->lazyLoad(self::LAZY_LOADER_USER, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUser(\Szurubooru\Entities\User $user)
|
||||||
|
{
|
||||||
|
$this->lazySave(self::LAZY_LOADER_USER, $user);
|
||||||
|
$this->userId = $user->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPost()
|
||||||
|
{
|
||||||
|
return $this->lazyLoad(self::LAZY_LOADER_POST, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPost(\Szurubooru\Entities\Post $post)
|
||||||
|
{
|
||||||
|
$this->lazySave(self::LAZY_LOADER_POST, $post);
|
||||||
|
$this->postId = $post->getId();
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ final class Post extends Entity
|
||||||
const LAZY_LOADER_RELATED_POSTS = 'relatedPosts';
|
const LAZY_LOADER_RELATED_POSTS = 'relatedPosts';
|
||||||
|
|
||||||
const META_TAG_COUNT = 'tagCount';
|
const META_TAG_COUNT = 'tagCount';
|
||||||
|
const META_FAV_COUNT = 'favCount';
|
||||||
|
|
||||||
protected $name;
|
protected $name;
|
||||||
protected $userId;
|
protected $userId;
|
||||||
|
@ -257,4 +258,9 @@ final class Post extends Entity
|
||||||
{
|
{
|
||||||
return $this->getMeta(self::META_TAG_COUNT, 0);
|
return $this->getMeta(self::META_TAG_COUNT, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getFavoriteCount()
|
||||||
|
{
|
||||||
|
return $this->getMeta(self::META_FAV_COUNT, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
60
src/Services/FavoritesService.php
Normal file
60
src/Services/FavoritesService.php
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
<?php
|
||||||
|
namespace Szurubooru\Services;
|
||||||
|
|
||||||
|
class FavoritesService
|
||||||
|
{
|
||||||
|
private $favoritesDao;
|
||||||
|
private $userDao;
|
||||||
|
private $transactionManager;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
\Szurubooru\Dao\FavoritesDao $favoritesDao,
|
||||||
|
\Szurubooru\Dao\UserDao $userDao,
|
||||||
|
\Szurubooru\Dao\TransactionManager $transactionManager)
|
||||||
|
{
|
||||||
|
$this->favoritesDao = $favoritesDao;
|
||||||
|
$this->userDao = $userDao;
|
||||||
|
$this->transactionManager = $transactionManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFavoriteUsers(\Szurubooru\Entities\Post $post)
|
||||||
|
{
|
||||||
|
$transactionFunc = function() use ($post)
|
||||||
|
{
|
||||||
|
$favorites = $this->favoritesDao->findByPost($post);
|
||||||
|
$userIds = [];
|
||||||
|
foreach ($favorites as $favorite)
|
||||||
|
{
|
||||||
|
$userIds[] = $favorite->getUserId();
|
||||||
|
}
|
||||||
|
return $this->userDao->findByIds($userIds);
|
||||||
|
};
|
||||||
|
return $this->transactionManager->rollback($transactionFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addFavorite(\Szurubooru\Entities\User $user, \Szurubooru\Entities\Post $post)
|
||||||
|
{
|
||||||
|
$transactionFunc = function() use ($user, $post)
|
||||||
|
{
|
||||||
|
$favorite = $this->favoritesDao->findByUserAndPost($user, $post);
|
||||||
|
if (!$favorite)
|
||||||
|
{
|
||||||
|
$favorite = new \Szurubooru\Entities\Favorite();
|
||||||
|
$favorite->setUser($user);
|
||||||
|
$favorite->setPost($post);
|
||||||
|
$this->favoritesDao->save($favorite);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return $this->transactionManager->commit($transactionFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteFavorite(\Szurubooru\Entities\User $user, \Szurubooru\Entities\Post $post)
|
||||||
|
{
|
||||||
|
$transactionFunc = function() use ($user, $post)
|
||||||
|
{
|
||||||
|
$favorite = $this->favoritesDao->findByUserAndPost($user, $post);
|
||||||
|
$this->favoritesDao->deleteById($favorite->getId());
|
||||||
|
};
|
||||||
|
return $this->transactionManager->commit($transactionFunc);
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,13 +44,21 @@ class PrivilegeService
|
||||||
public function assertPrivilege($privilege)
|
public function assertPrivilege($privilege)
|
||||||
{
|
{
|
||||||
if (!$this->hasPrivilege($privilege))
|
if (!$this->hasPrivilege($privilege))
|
||||||
throw new \DomainException('Unprivileged operation');
|
$this->fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function assertLoggedIn($userIdentifier)
|
public function assertLoggedIn($userIdentifier = null)
|
||||||
{
|
{
|
||||||
if (!$this->isLoggedIn($userIdentifier))
|
if ($userIdentifier)
|
||||||
throw new \DomainException('Unprivileged operation');
|
{
|
||||||
|
if (!$this->isLoggedIn($userIdentifier))
|
||||||
|
$this->fail();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!$this->authService->getLoggedInUser())
|
||||||
|
$this->fail();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isLoggedIn($userIdentifier)
|
public function isLoggedIn($userIdentifier)
|
||||||
|
@ -74,4 +82,9 @@ class PrivilegeService
|
||||||
throw new \InvalidArgumentException('Invalid user identifier.');
|
throw new \InvalidArgumentException('Invalid user identifier.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function fail()
|
||||||
|
{
|
||||||
|
throw new \DomainException('Unprivileged operation');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
42
src/Upgrades/Upgrade10.php
Normal file
42
src/Upgrades/Upgrade10.php
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
namespace Szurubooru\Upgrades;
|
||||||
|
|
||||||
|
class Upgrade10 implements IUpgrade
|
||||||
|
{
|
||||||
|
public function run(\Szurubooru\DatabaseConnection $databaseConnection)
|
||||||
|
{
|
||||||
|
$pdo = $databaseConnection->getPDO();
|
||||||
|
|
||||||
|
$pdo->exec('ALTER TABLE posts ADD COLUMN favCount INTEGER NOT NULL DEFAULT 0');
|
||||||
|
|
||||||
|
$pdo->exec('CREATE TABLE favorites
|
||||||
|
(
|
||||||
|
id INTEGER PRIMARY KEY NOT NULL,
|
||||||
|
userId INTEGER NOT NULL,
|
||||||
|
postId INTEGER NOT NULL,
|
||||||
|
UNIQUE (userId, postId)
|
||||||
|
)');
|
||||||
|
|
||||||
|
$pdo->exec('
|
||||||
|
CREATE TRIGGER favoritesDelete BEFORE DELETE ON favorites
|
||||||
|
FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
UPDATE posts SET favCount = favCount - 1 WHERE posts.id = OLD.postId;
|
||||||
|
END');
|
||||||
|
|
||||||
|
$pdo->exec('
|
||||||
|
CREATE TRIGGER favoritesInsert AFTER INSERT ON favorites
|
||||||
|
FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
UPDATE posts SET favCount = favCount + 1 WHERE posts.id = NEW.postId;
|
||||||
|
END');
|
||||||
|
|
||||||
|
$pdo->exec('
|
||||||
|
CREATE TRIGGER favoritesUpdate AFTER UPDATE ON favorites
|
||||||
|
FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
UPDATE posts SET favCount = favCount + 1 WHERE posts.id = NEW.postId;
|
||||||
|
UPDATE posts SET favCount = favCount - 1 WHERE posts.id = NEW.postId;
|
||||||
|
END');
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ return [
|
||||||
$container->get(\Szurubooru\Upgrades\Upgrade07::class),
|
$container->get(\Szurubooru\Upgrades\Upgrade07::class),
|
||||||
$container->get(\Szurubooru\Upgrades\Upgrade08::class),
|
$container->get(\Szurubooru\Upgrades\Upgrade08::class),
|
||||||
$container->get(\Szurubooru\Upgrades\Upgrade09::class),
|
$container->get(\Szurubooru\Upgrades\Upgrade09::class),
|
||||||
|
$container->get(\Szurubooru\Upgrades\Upgrade10::class),
|
||||||
];
|
];
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -37,6 +38,7 @@ return [
|
||||||
$container->get(\Szurubooru\Controllers\PostContentController::class),
|
$container->get(\Szurubooru\Controllers\PostContentController::class),
|
||||||
$container->get(\Szurubooru\Controllers\GlobalParamController::class),
|
$container->get(\Szurubooru\Controllers\GlobalParamController::class),
|
||||||
$container->get(\Szurubooru\Controllers\HistoryController::class),
|
$container->get(\Szurubooru\Controllers\HistoryController::class),
|
||||||
|
$container->get(\Szurubooru\Controllers\FavoritesController::class),
|
||||||
];
|
];
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
|
@ -25,30 +25,4 @@ abstract class AbstractDatabaseTestCase extends \Szurubooru\Tests\AbstractTestCa
|
||||||
if ($this->databaseConnection)
|
if ($this->databaseConnection)
|
||||||
$this->databaseConnection->close();
|
$this->databaseConnection->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function assertEntitiesEqual($expected, $actual)
|
|
||||||
{
|
|
||||||
if (!is_array($expected))
|
|
||||||
{
|
|
||||||
$expected = [$expected];
|
|
||||||
$actual = [$actual];
|
|
||||||
}
|
|
||||||
$this->assertEquals(count($expected), count($actual), 'Unmatching array sizes');
|
|
||||||
$this->assertEquals(array_keys($expected), array_keys($actual), 'Unmatching array keys');
|
|
||||||
foreach (array_keys($expected) as $key)
|
|
||||||
{
|
|
||||||
if ($expected[$key] === null)
|
|
||||||
{
|
|
||||||
$this->assertNull($actual[$key]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$expected[$key]->resetLazyLoaders();
|
|
||||||
$expected[$key]->resetMeta();
|
|
||||||
$actual[$key]->resetLazyLoaders();
|
|
||||||
$actual[$key]->resetMeta();
|
|
||||||
$this->assertEquals($expected[$key], $actual[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,39 +3,6 @@ namespace Szurubooru\Tests;
|
||||||
|
|
||||||
abstract class AbstractTestCase extends \PHPUnit_Framework_TestCase
|
abstract class AbstractTestCase extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
public function mock($className)
|
|
||||||
{
|
|
||||||
return $this->getMockBuilder($className)->disableOriginalConstructor()->getMock();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function mockConfig($dataPath = null, $publicDataPath = null)
|
|
||||||
{
|
|
||||||
return new ConfigMock($dataPath, $publicDataPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function mockTransactionManager()
|
|
||||||
{
|
|
||||||
return new TransactionManagerMock($this->mock(\Szurubooru\DatabaseConnection::class));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function createTestDirectory()
|
|
||||||
{
|
|
||||||
$path = $this->getTestDirectoryPath();
|
|
||||||
if (!file_exists($path))
|
|
||||||
mkdir($path, 0777, true);
|
|
||||||
return $path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTestFile($fileName)
|
|
||||||
{
|
|
||||||
return file_get_contents($this->getTestFilePath($fileName));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTestFilePath($fileName)
|
|
||||||
{
|
|
||||||
return __DIR__ . DIRECTORY_SEPARATOR . 'test_files' . DIRECTORY_SEPARATOR . $fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function setUp()
|
protected function setUp()
|
||||||
{
|
{
|
||||||
\Szurubooru\Injector::init();
|
\Szurubooru\Injector::init();
|
||||||
|
@ -46,6 +13,66 @@ abstract class AbstractTestCase extends \PHPUnit_Framework_TestCase
|
||||||
$this->cleanTestDirectory();
|
$this->cleanTestDirectory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function mock($className)
|
||||||
|
{
|
||||||
|
return $this->getMockBuilder($className)->disableOriginalConstructor()->getMock();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function mockConfig($dataPath = null, $publicDataPath = null)
|
||||||
|
{
|
||||||
|
return new ConfigMock($dataPath, $publicDataPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function mockTransactionManager()
|
||||||
|
{
|
||||||
|
return new TransactionManagerMock($this->mock(\Szurubooru\DatabaseConnection::class));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createTestDirectory()
|
||||||
|
{
|
||||||
|
$path = $this->getTestDirectoryPath();
|
||||||
|
if (!file_exists($path))
|
||||||
|
mkdir($path, 0777, true);
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTestFile($fileName)
|
||||||
|
{
|
||||||
|
return file_get_contents($this->getTestFilePath($fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTestFilePath($fileName)
|
||||||
|
{
|
||||||
|
return __DIR__ . DIRECTORY_SEPARATOR . 'test_files' . DIRECTORY_SEPARATOR . $fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function assertEntitiesEqual($expected, $actual)
|
||||||
|
{
|
||||||
|
if (!is_array($expected))
|
||||||
|
{
|
||||||
|
$expected = [$expected];
|
||||||
|
$actual = [$actual];
|
||||||
|
}
|
||||||
|
$this->assertEquals(count($expected), count($actual), 'Unmatching array sizes');
|
||||||
|
$this->assertEquals(array_keys($expected), array_keys($actual), 'Unmatching array keys');
|
||||||
|
foreach (array_keys($expected) as $key)
|
||||||
|
{
|
||||||
|
if ($expected[$key] === null)
|
||||||
|
{
|
||||||
|
$this->assertNull($actual[$key]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->assertNotNull($actual[$key]);
|
||||||
|
$expected[$key]->resetLazyLoaders();
|
||||||
|
$expected[$key]->resetMeta();
|
||||||
|
$actual[$key]->resetLazyLoaders();
|
||||||
|
$actual[$key]->resetMeta();
|
||||||
|
$this->assertEquals($expected[$key], $actual[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function getTestDirectoryPath()
|
private function getTestDirectoryPath()
|
||||||
{
|
{
|
||||||
return __DIR__ . DIRECTORY_SEPARATOR . 'files';
|
return __DIR__ . DIRECTORY_SEPARATOR . 'files';
|
||||||
|
|
82
tests/Dao/FavoritesDaoTest.php
Normal file
82
tests/Dao/FavoritesDaoTest.php
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
<?php
|
||||||
|
namespace Szurubooru\Tests\Dao;
|
||||||
|
|
||||||
|
class FavoritesDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase
|
||||||
|
{
|
||||||
|
private $userDaoMock;
|
||||||
|
private $postDaoMock;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->userDaoMock = $this->mock(\Szurubooru\Dao\UserDao::class);
|
||||||
|
$this->postDaoMock = $this->mock(\Szurubooru\Dao\PostDao::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSaving()
|
||||||
|
{
|
||||||
|
$user = new \Szurubooru\Entities\User(1);
|
||||||
|
$user->setName('olivia');
|
||||||
|
|
||||||
|
$post = new \Szurubooru\Entities\Post(2);
|
||||||
|
$post->setName('sword');
|
||||||
|
|
||||||
|
$favorite = new \Szurubooru\Entities\Favorite();
|
||||||
|
$favorite->setUser($user);
|
||||||
|
$favorite->setPost($post);
|
||||||
|
$favoritesDao = $this->getFavoritesDao();
|
||||||
|
$favoritesDao->save($favorite);
|
||||||
|
|
||||||
|
$this->userDaoMock->expects($this->once())->method('findById')->with(1)->willReturn($user);
|
||||||
|
$this->postDaoMock->expects($this->once())->method('findById')->with(2)->willReturn($post);
|
||||||
|
|
||||||
|
$savedFavorite = $favoritesDao->findById($favorite->getId());
|
||||||
|
$this->assertEquals(1, $savedFavorite->getUserId());
|
||||||
|
$this->assertEquals(2, $savedFavorite->getPostId());
|
||||||
|
$this->assertEntitiesEqual($user, $savedFavorite->getUser());
|
||||||
|
$this->assertEntitiesEqual($post, $savedFavorite->getPost());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFindingByUserAndPost()
|
||||||
|
{
|
||||||
|
$post1 = new \Szurubooru\Entities\Post(1);
|
||||||
|
$post2 = new \Szurubooru\Entities\Post(2);
|
||||||
|
$user1 = new \Szurubooru\Entities\User(3);
|
||||||
|
$user2 = new \Szurubooru\Entities\User(4);
|
||||||
|
|
||||||
|
$fav1 = new \Szurubooru\Entities\Favorite();
|
||||||
|
$fav1->setUser($user1);
|
||||||
|
$fav1->setPost($post1);
|
||||||
|
|
||||||
|
$fav2 = new \Szurubooru\Entities\Favorite();
|
||||||
|
$fav2->setUser($user2);
|
||||||
|
$fav2->setPost($post2);
|
||||||
|
|
||||||
|
$fav3 = new \Szurubooru\Entities\Favorite();
|
||||||
|
$fav3->setUser($user1);
|
||||||
|
$fav3->setPost($post2);
|
||||||
|
|
||||||
|
$favoritesDao = $this->getFavoritesDao();
|
||||||
|
$favoritesDao->save($fav1);
|
||||||
|
$favoritesDao->save($fav2);
|
||||||
|
$favoritesDao->save($fav3);
|
||||||
|
|
||||||
|
$this->assertEntitiesEqual($fav1, $favoritesDao->findByUserAndPost($user1, $post1));
|
||||||
|
$this->assertEntitiesEqual($fav2, $favoritesDao->findByUserAndPost($user2, $post2));
|
||||||
|
$this->assertEntitiesEqual($fav3, $favoritesDao->findByUserAndPost($user1, $post2));
|
||||||
|
$this->assertNull($favoritesDao->findByUserAndPost($user2, $post1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findByPost(\Szurubooru\Entities\Post $post)
|
||||||
|
{
|
||||||
|
return $this->findOneBy('postId', $post->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFavoritesDao()
|
||||||
|
{
|
||||||
|
return new \Szurubooru\Dao\FavoritesDao(
|
||||||
|
$this->databaseConnection,
|
||||||
|
$this->userDaoMock,
|
||||||
|
$this->postDaoMock);
|
||||||
|
}
|
||||||
|
}
|
83
tests/Services/FavoritesServiceTest.php
Normal file
83
tests/Services/FavoritesServiceTest.php
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<?php
|
||||||
|
namespace Szurubooru\Tests\Services;
|
||||||
|
|
||||||
|
final class FavoritesServiceTest extends \Szurubooru\Tests\AbstractTestCase
|
||||||
|
{
|
||||||
|
private $favoritesDaoMock;
|
||||||
|
private $userDaoMock;
|
||||||
|
private $transactionManagerMock;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->favoritesDaoMock = $this->mock(\Szurubooru\Dao\FavoritesDao::class);
|
||||||
|
$this->userDaoMock = $this->mock(\Szurubooru\Dao\UserDao::class);
|
||||||
|
$this->transactionManagerMock = $this->mockTransactionManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddingExisting()
|
||||||
|
{
|
||||||
|
$user = new \Szurubooru\Entities\User();
|
||||||
|
$post = new \Szurubooru\Entities\Post();
|
||||||
|
$fav = new \Szurubooru\Entities\Favorite(3);
|
||||||
|
$this->favoritesDaoMock->expects($this->once())->method('findByUserAndPost')->with($user, $post)->willReturn($fav);
|
||||||
|
$this->favoritesDaoMock->expects($this->never())->method('save');
|
||||||
|
|
||||||
|
$favoritesService = $this->getFavoritesService();
|
||||||
|
$favoritesService->addFavorite($user, $post);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAdding()
|
||||||
|
{
|
||||||
|
$user = new \Szurubooru\Entities\User(1);
|
||||||
|
$post = new \Szurubooru\Entities\Post(2);
|
||||||
|
$fav = new \Szurubooru\Entities\Favorite();
|
||||||
|
$fav->setUserId($user->getId());
|
||||||
|
$fav->setPostId($post->getId());
|
||||||
|
$this->favoritesDaoMock->expects($this->once())->method('findByUserAndPost')->with($user, $post)->willReturn(null);
|
||||||
|
$this->favoritesDaoMock->expects($this->once())->method('save')->with($this->callback(
|
||||||
|
function($subject) use ($fav)
|
||||||
|
{
|
||||||
|
$this->assertEntitiesEqual($fav, $subject);
|
||||||
|
return true;
|
||||||
|
}));
|
||||||
|
|
||||||
|
$favoritesService = $this->getFavoritesService();
|
||||||
|
$favoritesService->addFavorite($user, $post);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDeleting()
|
||||||
|
{
|
||||||
|
$user = new \Szurubooru\Entities\User();
|
||||||
|
$post = new \Szurubooru\Entities\Post();
|
||||||
|
$fav = new \Szurubooru\Entities\Favorite(3);
|
||||||
|
$this->favoritesDaoMock->expects($this->once())->method('findByUserAndPost')->with($user, $post)->willReturn($fav);
|
||||||
|
$this->favoritesDaoMock->expects($this->once())->method('deleteById')->with($fav->getId());
|
||||||
|
|
||||||
|
$favoritesService = $this->getFavoritesService();
|
||||||
|
$favoritesService->deleteFavorite($user, $post);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGettingByPost()
|
||||||
|
{
|
||||||
|
$post = new \Szurubooru\Entities\Post();
|
||||||
|
$fav1 = new \Szurubooru\Entities\Favorite();
|
||||||
|
$fav2 = new \Szurubooru\Entities\Favorite();
|
||||||
|
$fav1->setUser(new \Szurubooru\Entities\User(1));
|
||||||
|
$fav2->setUser(new \Szurubooru\Entities\User(2));
|
||||||
|
|
||||||
|
$this->favoritesDaoMock->expects($this->once())->method('findByPost')->with($post)->willReturn([$fav1, $fav2]);
|
||||||
|
$this->userDaoMock->expects($this->once())->method('findByIds')->with([1, 2]);
|
||||||
|
|
||||||
|
$favoritesService = $this->getFavoritesService();
|
||||||
|
$favoritesService->getFavoriteUsers($post);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFavoritesService()
|
||||||
|
{
|
||||||
|
return new \Szurubooru\Services\FavoritesService(
|
||||||
|
$this->favoritesDaoMock,
|
||||||
|
$this->userDaoMock,
|
||||||
|
$this->transactionManagerMock);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue