diff --git a/app/Entity.php b/app/Entity.php
index 186059f00..e8deddf0a 100644
--- a/app/Entity.php
+++ b/app/Entity.php
@@ -4,6 +4,8 @@
class Entity extends Ownable
{
+ protected $fieldsToSearch = ['name', 'description'];
+
/**
* Compares this entity to another given entity.
* Matches by comparing class and id.
@@ -157,7 +159,7 @@ class Entity extends Ownable
* @param string[] array $wheres
* @return mixed
*/
- public function fullTextSearchQuery($fieldsToSearch, $terms, $wheres = [])
+ public function fullTextSearchQuery($terms, $wheres = [])
{
$exactTerms = [];
$fuzzyTerms = [];
@@ -181,16 +183,16 @@ class Entity extends Ownable
// Perform fulltext search if relevant terms exist.
if ($isFuzzy) {
$termString = implode(' ', $fuzzyTerms);
- $fields = implode(',', $fieldsToSearch);
+ $fields = implode(',', $this->fieldsToSearch);
$search = $search->selectRaw('*, MATCH(name) AGAINST(? IN BOOLEAN MODE) AS title_relevance', [$termString]);
$search = $search->whereRaw('MATCH(' . $fields . ') AGAINST(? IN BOOLEAN MODE)', [$termString]);
}
// Ensure at least one exact term matches if in search
if (count($exactTerms) > 0) {
- $search = $search->where(function ($query) use ($exactTerms, $fieldsToSearch) {
+ $search = $search->where(function ($query) use ($exactTerms) {
foreach ($exactTerms as $exactTerm) {
- foreach ($fieldsToSearch as $field) {
+ foreach ($this->fieldsToSearch as $field) {
$query->orWhere($field, 'like', $exactTerm);
}
}
diff --git a/app/Http/Controllers/ChapterController.php b/app/Http/Controllers/ChapterController.php
index e71ed4d98..d239b08cc 100644
--- a/app/Http/Controllers/ChapterController.php
+++ b/app/Http/Controllers/ChapterController.php
@@ -118,7 +118,7 @@ class ChapterController extends Controller
$chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
$this->checkOwnablePermission('chapter-update', $chapter);
if ($chapter->name !== $request->get('name')) {
- $chapter->slug = $this->chapterRepo->findSuitableSlug($request->get('name'), $chapter->book->id, $chapter->id);
+ $chapter->slug = $this->entityRepo->findSuitableSlug('chapter', $request->get('name'), $chapter->id, $chapter->book->id);
}
$chapter->fill($request->all());
$chapter->updated_by = user()->id;
diff --git a/app/Http/Controllers/PageController.php b/app/Http/Controllers/PageController.php
index 0d6678e04..5a33ecb37 100644
--- a/app/Http/Controllers/PageController.php
+++ b/app/Http/Controllers/PageController.php
@@ -26,6 +26,7 @@ class PageController extends Controller
/**
* PageController constructor.
+ * @param EntityRepo $entityRepo
* @param PageRepo $pageRepo
* @param BookRepo $bookRepo
* @param ChapterRepo $chapterRepo
diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php
index bb70b0f88..37aaccece 100644
--- a/app/Http/Controllers/SearchController.php
+++ b/app/Http/Controllers/SearchController.php
@@ -1,30 +1,22 @@
pageRepo = $pageRepo;
- $this->bookRepo = $bookRepo;
- $this->chapterRepo = $chapterRepo;
+ $this->entityRepo = $entityRepo;
$this->viewService = $viewService;
parent::__construct();
}
@@ -42,9 +34,9 @@ class SearchController extends Controller
}
$searchTerm = $request->get('term');
$paginationAppends = $request->only('term');
- $pages = $this->pageRepo->getBySearch($searchTerm, [], 20, $paginationAppends);
- $books = $this->bookRepo->getBySearch($searchTerm, 10, $paginationAppends);
- $chapters = $this->chapterRepo->getBySearch($searchTerm, [], 10, $paginationAppends);
+ $pages = $this->entityRepo->getBySearch('page', $searchTerm, [], 20, $paginationAppends);
+ $books = $this->entityRepo->getBySearch('book', $searchTerm, [], 10, $paginationAppends);
+ $chapters = $this->entityRepo->getBySearch('chapter', $searchTerm, [], 10, $paginationAppends);
$this->setPageTitle(trans('entities.search_for_term', ['term' => $searchTerm]));
return view('search/all', [
'pages' => $pages,
@@ -65,7 +57,7 @@ class SearchController extends Controller
$searchTerm = $request->get('term');
$paginationAppends = $request->only('term');
- $pages = $this->pageRepo->getBySearch($searchTerm, [], 20, $paginationAppends);
+ $pages = $this->entityRepo->getBySearch('page', $searchTerm, [], 20, $paginationAppends);
$this->setPageTitle(trans('entities.search_page_for_term', ['term' => $searchTerm]));
return view('search/entity-search-list', [
'entities' => $pages,
@@ -85,7 +77,7 @@ class SearchController extends Controller
$searchTerm = $request->get('term');
$paginationAppends = $request->only('term');
- $chapters = $this->chapterRepo->getBySearch($searchTerm, [], 20, $paginationAppends);
+ $chapters = $this->entityRepo->getBySearch('chapter', $searchTerm, [], 20, $paginationAppends);
$this->setPageTitle(trans('entities.search_chapter_for_term', ['term' => $searchTerm]));
return view('search/entity-search-list', [
'entities' => $chapters,
@@ -105,7 +97,7 @@ class SearchController extends Controller
$searchTerm = $request->get('term');
$paginationAppends = $request->only('term');
- $books = $this->bookRepo->getBySearch($searchTerm, 20, $paginationAppends);
+ $books = $this->entityRepo->getBySearch('book', $searchTerm, [], 20, $paginationAppends);
$this->setPageTitle(trans('entities.search_book_for_term', ['term' => $searchTerm]));
return view('search/entity-search-list', [
'entities' => $books,
@@ -128,8 +120,8 @@ class SearchController extends Controller
}
$searchTerm = $request->get('term');
$searchWhereTerms = [['book_id', '=', $bookId]];
- $pages = $this->pageRepo->getBySearch($searchTerm, $searchWhereTerms);
- $chapters = $this->chapterRepo->getBySearch($searchTerm, $searchWhereTerms);
+ $pages = $this->entityRepo->getBySearch('page', $searchTerm, $searchWhereTerms);
+ $chapters = $this->entityRepo->getBySearch('chapter', $searchTerm, $searchWhereTerms);
return view('search/book', ['pages' => $pages, 'chapters' => $chapters, 'searchTerm' => $searchTerm]);
}
@@ -148,9 +140,11 @@ class SearchController extends Controller
// Search for entities otherwise show most popular
if ($searchTerm !== false) {
- if ($entityTypes->contains('page')) $entities = $entities->merge($this->pageRepo->getBySearch($searchTerm)->items());
- if ($entityTypes->contains('chapter')) $entities = $entities->merge($this->chapterRepo->getBySearch($searchTerm)->items());
- if ($entityTypes->contains('book')) $entities = $entities->merge($this->bookRepo->getBySearch($searchTerm)->items());
+ foreach (['page', 'chapter', 'book'] as $entityType) {
+ if ($entityTypes->contains($entityType)) {
+ $entities = $entities->merge($this->entityRepo->getBySearch($entityType, $searchTerm)->items());
+ }
+ }
$entities = $entities->sortByDesc('title_relevance');
} else {
$entityNames = $entityTypes->map(function ($type) {
diff --git a/app/Page.php b/app/Page.php
index 38f95a3b1..b24e7778a 100644
--- a/app/Page.php
+++ b/app/Page.php
@@ -9,6 +9,8 @@ class Page extends Entity
protected $with = ['book'];
+ protected $fieldsToSearch = ['name', 'text'];
+
/**
* Converts this page into a simplified array.
* @return mixed
diff --git a/app/Repos/BookRepo.php b/app/Repos/BookRepo.php
index ebfda3fa4..3043d2916 100644
--- a/app/Repos/BookRepo.php
+++ b/app/Repos/BookRepo.php
@@ -27,7 +27,7 @@ class BookRepo extends EntityRepo
public function createFromInput($input)
{
$book = $this->book->newInstance($input);
- $book->slug = $this->findSuitableSlug($book->name);
+ $book->slug = $this->findSuitableSlug('book', $book->name);
$book->created_by = user()->id;
$book->updated_by = user()->id;
$book->save();
@@ -44,7 +44,7 @@ class BookRepo extends EntityRepo
public function updateFromInput(Book $book, $input)
{
if ($book->name !== $input['name']) {
- $book->slug = $this->findSuitableSlug($input['name'], $book->id);
+ $book->slug = $this->findSuitableSlug('book', $input['name'], $book->id);
}
$book->fill($input);
$book->updated_by = user()->id;
@@ -83,36 +83,6 @@ class BookRepo extends EntityRepo
return $lastElem ? $lastElem->priority + 1 : 0;
}
- /**
- * @param string $slug
- * @param bool|false $currentId
- * @return bool
- */
- public function doesSlugExist($slug, $currentId = false)
- {
- $query = $this->book->where('slug', '=', $slug);
- if ($currentId) {
- $query = $query->where('id', '!=', $currentId);
- }
- return $query->count() > 0;
- }
-
- /**
- * Provides a suitable slug for the given book name.
- * Ensures the returned slug is unique in the system.
- * @param string $name
- * @param bool|false $currentId
- * @return string
- */
- public function findSuitableSlug($name, $currentId = false)
- {
- $slug = $this->nameToSlug($name);
- while ($this->doesSlugExist($slug, $currentId)) {
- $slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
- }
- return $slug;
- }
-
/**
* Get all child objects of a book.
* Returns a sorted collection of Pages and Chapters.
@@ -166,26 +136,4 @@ class BookRepo extends EntityRepo
});
}
- /**
- * Get books by search term.
- * @param $term
- * @param int $count
- * @param array $paginationAppends
- * @return mixed
- */
- public function getBySearch($term, $count = 20, $paginationAppends = [])
- {
- $terms = $this->prepareSearchTerms($term);
- $bookQuery = $this->permissionService->enforceBookRestrictions($this->book->fullTextSearchQuery(['name', 'description'], $terms));
- $bookQuery = $this->addAdvancedSearchQueries($bookQuery, $term);
- $books = $bookQuery->paginate($count)->appends($paginationAppends);
- $words = join('|', explode(' ', preg_quote(trim($term), '/')));
- foreach ($books as $book) {
- //highlight
- $result = preg_replace('#' . $words . '#iu', "\$0", $book->getExcerpt(100));
- $book->searchSnippet = $result;
- }
- return $books;
- }
-
}
\ No newline at end of file
diff --git a/app/Repos/ChapterRepo.php b/app/Repos/ChapterRepo.php
index 861bb72fc..afbf312da 100644
--- a/app/Repos/ChapterRepo.php
+++ b/app/Repos/ChapterRepo.php
@@ -45,7 +45,7 @@ class ChapterRepo extends EntityRepo
public function createFromInput($input, Book $book)
{
$chapter = $this->chapter->newInstance($input);
- $chapter->slug = $this->findSuitableSlug($chapter->name, $book->id);
+ $chapter->slug = $this->findSuitableSlug('chapter', $chapter->name, false, $book->id);
$chapter->created_by = user()->id;
$chapter->updated_by = user()->id;
$chapter = $book->chapters()->save($chapter);
@@ -72,38 +72,6 @@ class ChapterRepo extends EntityRepo
$chapter->delete();
}
- /**
- * Check if a chapter's slug exists.
- * @param $slug
- * @param $bookId
- * @param bool|false $currentId
- * @return bool
- */
- public function doesSlugExist($slug, $bookId, $currentId = false)
- {
- $query = $this->chapter->where('slug', '=', $slug)->where('book_id', '=', $bookId);
- if ($currentId) {
- $query = $query->where('id', '!=', $currentId);
- }
- return $query->count() > 0;
- }
-
- /**
- * Finds a suitable slug for the provided name.
- * Checks database to prevent duplicate slugs.
- * @param $name
- * @param $bookId
- * @param bool|false $currentId
- * @return string
- */
- public function findSuitableSlug($name, $bookId, $currentId = false)
- {
- $slug = $this->nameToSlug($name);
- while ($this->doesSlugExist($slug, $bookId, $currentId)) {
- $slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
- }
- return $slug;
- }
/**
* Get a new priority value for a new page to be added
@@ -117,29 +85,6 @@ class ChapterRepo extends EntityRepo
return $lastPage !== null ? $lastPage->priority + 1 : 0;
}
- /**
- * Get chapters by the given search term.
- * @param string $term
- * @param array $whereTerms
- * @param int $count
- * @param array $paginationAppends
- * @return mixed
- */
- public function getBySearch($term, $whereTerms = [], $count = 20, $paginationAppends = [])
- {
- $terms = $this->prepareSearchTerms($term);
- $chapterQuery = $this->permissionService->enforceChapterRestrictions($this->chapter->fullTextSearchQuery(['name', 'description'], $terms, $whereTerms));
- $chapterQuery = $this->addAdvancedSearchQueries($chapterQuery, $term);
- $chapters = $chapterQuery->paginate($count)->appends($paginationAppends);
- $words = join('|', explode(' ', preg_quote(trim($term), '/')));
- foreach ($chapters as $chapter) {
- //highlight
- $result = preg_replace('#' . $words . '#iu', "\$0", $chapter->getExcerpt(100));
- $chapter->searchSnippet = $result;
- }
- return $chapters;
- }
-
/**
* Changes the book relation of this chapter.
* @param $bookId
@@ -155,7 +100,7 @@ class ChapterRepo extends EntityRepo
$activity->book_id = $bookId;
$activity->save();
}
- $chapter->slug = $this->findSuitableSlug($chapter->name, $bookId, $chapter->id);
+ $chapter->slug = $this->findSuitableSlug('chapter', $chapter->name, $chapter->id, $bookId);
$chapter->save();
// Update all child pages
foreach ($chapter->pages as $page) {
diff --git a/app/Repos/EntityRepo.php b/app/Repos/EntityRepo.php
index 19beebc77..40fdea54e 100644
--- a/app/Repos/EntityRepo.php
+++ b/app/Repos/EntityRepo.php
@@ -238,6 +238,83 @@ class EntityRepo
->skip($count * $page)->take($count)->get();
}
+ public function getBySearch($type, $term, $whereTerms = [], $count = 20, $paginationAppends = [])
+ {
+ $terms = $this->prepareSearchTerms($term);
+ $q = $this->permissionService->enforceChapterRestrictions($this->getEntity($type)->fullTextSearchQuery($terms, $whereTerms));
+ $q = $this->addAdvancedSearchQueries($q, $term);
+ $entities = $q->paginate($count)->appends($paginationAppends);
+ $words = join('|', explode(' ', preg_quote(trim($term), '/')));
+
+ // Highlight page content
+ if ($type === 'page') {
+ //lookahead/behind assertions ensures cut between words
+ $s = '\s\x00-/:-@\[-`{-~'; //character set for start/end of words
+
+ foreach ($entities as $page) {
+ preg_match_all('#(?<=[' . $s . ']).{1,30}((' . $words . ').{1,30})+(?=[' . $s . '])#uis', $page->text, $matches, PREG_SET_ORDER);
+ //delimiter between occurrences
+ $results = [];
+ foreach ($matches as $line) {
+ $results[] = htmlspecialchars($line[0], 0, 'UTF-8');
+ }
+ $matchLimit = 6;
+ if (count($results) > $matchLimit) $results = array_slice($results, 0, $matchLimit);
+ $result = join('... ', $results);
+
+ //highlight
+ $result = preg_replace('#' . $words . '#iu', "\$0", $result);
+ if (strlen($result) < 5) $result = $page->getExcerpt(80);
+
+ $page->searchSnippet = $result;
+ }
+ return $entities;
+ }
+
+ // Highlight chapter/book content
+ foreach ($entities as $entity) {
+ //highlight
+ $result = preg_replace('#' . $words . '#iu', "\$0", $entity->getExcerpt(100));
+ $entity->searchSnippet = $result;
+ }
+ return $entities;
+ }
+
+ /**
+ * Find a suitable slug for an entity.
+ * @param string $type
+ * @param string $name
+ * @param bool|integer $currentId
+ * @param bool|integer $bookId Only pass if type is not a book
+ * @return string
+ */
+ public function findSuitableSlug($type, $name, $currentId = false, $bookId = false)
+ {
+ $slug = $this->nameToSlug($name);
+ while ($this->slugExists($type, $slug, $currentId, $bookId)) {
+ $slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
+ }
+ return $slug;
+ }
+
+ /**
+ * Check if a slug already exists in the database.
+ * @param string $type
+ * @param string $slug
+ * @param bool|integer $currentId
+ * @param bool|integer $bookId
+ * @return bool
+ */
+ protected function slugExists($type, $slug, $currentId = false, $bookId = false)
+ {
+ $query = $this->getEntity($type)->where('slug', '=', $slug);
+ if (strtolower($type) === 'page' || strtolower($type) === 'chapter') {
+ $query = $query->where('book_id', '=', $bookId);
+ }
+ if ($currentId) $query = $query->where('id', '!=', $currentId);
+ return $query->count() > 0;
+ }
+
/**
* Updates entity restrictions from a request
* @param $request
diff --git a/app/Repos/PageRepo.php b/app/Repos/PageRepo.php
index f16ea6b6d..699e6ecc5 100644
--- a/app/Repos/PageRepo.php
+++ b/app/Repos/PageRepo.php
@@ -65,17 +65,6 @@ class PageRepo extends EntityRepo
return $revision !== null ? $revision->page : null;
}
- /**
- * Get a new Page instance from the given input.
- * @param $input
- * @return Page
- */
- public function newFromInput($input)
- {
- $page = $this->page->fill($input);
- return $page;
- }
-
/**
* Count the pages with a particular slug within a book.
* @param $slug
@@ -103,7 +92,7 @@ class PageRepo extends EntityRepo
$this->tagRepo->saveTagsToEntity($draftPage, $input['tags']);
}
- $draftPage->slug = $this->findSuitableSlug($draftPage->name, $draftPage->book->id);
+ $draftPage->slug = $this->findSuitableSlug('page', $draftPage->name, false, $draftPage->book->id);
$draftPage->html = $this->formatHtml($input['html']);
$draftPage->text = strip_tags($draftPage->html);
$draftPage->draft = false;
@@ -222,50 +211,6 @@ class PageRepo extends EntityRepo
}
- /**
- * Gets pages by a search term.
- * Highlights page content for showing in results.
- * @param string $term
- * @param array $whereTerms
- * @param int $count
- * @param array $paginationAppends
- * @return mixed
- */
- public function getBySearch($term, $whereTerms = [], $count = 20, $paginationAppends = [])
- {
- $terms = $this->prepareSearchTerms($term);
- $pageQuery = $this->permissionService->enforcePageRestrictions($this->page->fullTextSearchQuery(['name', 'text'], $terms, $whereTerms));
- $pageQuery = $this->addAdvancedSearchQueries($pageQuery, $term);
- $pages = $pageQuery->paginate($count)->appends($paginationAppends);
-
- // Add highlights to page text.
- $words = join('|', explode(' ', preg_quote(trim($term), '/')));
- //lookahead/behind assertions ensures cut between words
- $s = '\s\x00-/:-@\[-`{-~'; //character set for start/end of words
-
- foreach ($pages as $page) {
- preg_match_all('#(?<=[' . $s . ']).{1,30}((' . $words . ').{1,30})+(?=[' . $s . '])#uis', $page->text, $matches, PREG_SET_ORDER);
- //delimiter between occurrences
- $results = [];
- foreach ($matches as $line) {
- $results[] = htmlspecialchars($line[0], 0, 'UTF-8');
- }
- $matchLimit = 6;
- if (count($results) > $matchLimit) {
- $results = array_slice($results, 0, $matchLimit);
- }
- $result = join('... ', $results);
-
- //highlight
- $result = preg_replace('#' . $words . '#iu', "\$0", $result);
- if (strlen($result) < 5) {
- $result = $page->getExcerpt(80);
- }
- $page->searchSnippet = $result;
- }
- return $pages;
- }
-
/**
* Search for image usage.
* @param $imageString
@@ -297,7 +242,7 @@ class PageRepo extends EntityRepo
// Prevent slug being updated if no name change
if ($page->name !== $input['name']) {
- $page->slug = $this->findSuitableSlug($input['name'], $book_id, $page->id);
+ $page->slug = $this->findSuitableSlug('page', $input['name'], $page->id, $book_id);
}
// Save page tags if present
@@ -337,7 +282,7 @@ class PageRepo extends EntityRepo
$this->saveRevision($page);
$revision = $this->getRevisionById($revisionId);
$page->fill($revision->toArray());
- $page->slug = $this->findSuitableSlug($page->name, $book->id, $page->id);
+ $page->slug = $this->findSuitableSlug('page', $page->name, $page->id, $book->id);
$page->text = strip_tags($page->html);
$page->updated_by = user()->id;
$page->save();
@@ -529,20 +474,6 @@ class PageRepo extends EntityRepo
return $this->pageRevision->findOrFail($id);
}
- /**
- * Checks if a slug exists within a book already.
- * @param $slug
- * @param $bookId
- * @param bool|false $currentId
- * @return bool
- */
- public function doesSlugExist($slug, $bookId, $currentId = false)
- {
- $query = $this->page->where('slug', '=', $slug)->where('book_id', '=', $bookId);
- if ($currentId) $query = $query->where('id', '!=', $currentId);
- return $query->count() > 0;
- }
-
/**
* Changes the related book for the specified page.
* Changes the book id of any relations to the page that store the book id.
@@ -557,7 +488,7 @@ class PageRepo extends EntityRepo
$activity->book_id = $bookId;
$activity->save();
}
- $page->slug = $this->findSuitableSlug($page->name, $bookId, $page->id);
+ $page->slug = $this->findSuitableSlug('page', $page->name, $page->id, $bookId);
$page->save();
return $page;
}
@@ -578,22 +509,6 @@ class PageRepo extends EntityRepo
$this->permissionService->buildJointPermissionsForEntity($book);
}
- /**
- * Gets a suitable slug for the resource
- * @param string $name
- * @param int $bookId
- * @param bool|false $currentId
- * @return string
- */
- public function findSuitableSlug($name, $bookId, $currentId = false)
- {
- $slug = $this->nameToSlug($name);
- while ($this->doesSlugExist($slug, $bookId, $currentId)) {
- $slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
- }
- return $slug;
- }
-
/**
* Destroy a given page along with its dependencies.
* @param $page