Add position for book in collection

This commit is contained in:
2026-04-11 17:26:21 +02:00
parent 36a21c8891
commit d2fe3bf34f
14 changed files with 200 additions and 58 deletions

View File

@@ -132,18 +132,18 @@ INSERT INTO collections(name, user_id) VALUES ('Nouvelles',(SELECT id FROM users
INSERT INTO collections(name, user_id) VALUES ('Non fiction',(SELECT id FROM users WHERE name = 'demo2')); INSERT INTO collections(name, user_id) VALUES ('Non fiction',(SELECT id FROM users WHERE name = 'demo2'));
INSERT INTO collections(name, user_id) VALUES ('Empty',(SELECT id FROM users WHERE name = 'demo')); INSERT INTO collections(name, user_id) VALUES ('Empty',(SELECT id FROM users WHERE name = 'demo'));
INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Nord')); INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Nord'), 1);
INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Gargantua')); INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Gargantua'), 2);
INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Duo')); INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Duo'), 3);
INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Un barrage contre le Pacifique')); INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Un barrage contre le Pacifique'), 4);
INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Rigodon')); INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Rigodon'), 5);
INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Les dieux ont soif')); INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Les dieux ont soif'), 6);
INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Dojoji et autres nouvelles')); INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Dojoji et autres nouvelles'), 1);
INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Le meurtre d''O-tsuya')); INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Le meurtre d''O-tsuya'), 2);
INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Le coup de pistolet')); INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Le coup de pistolet'), 3);
INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Duo')); INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Duo'), 3);
INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Non fiction'), (SELECT id FROM books WHERE title = 'Recherches philosophiques')); INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Non fiction'), (SELECT id FROM books WHERE title = 'Recherches philosophiques'), 1);
INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Non fiction'), (SELECT id FROM books WHERE title = 'De sang-froid')); INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Non fiction'), (SELECT id FROM books WHERE title = 'De sang-froid'), 2);
INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Non fiction'), (SELECT id FROM books WHERE title = 'The Life of Jesus')); INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Non fiction'), (SELECT id FROM books WHERE title = 'The Life of Jesus'), 3);

View File

@@ -164,6 +164,7 @@ async function importInventaireEdition(inventaireid) {
</button> </button>
</div> </div>
</div> </div>
<slot></slot>
</div> </div>
</template> </template>

View File

@@ -1,7 +1,7 @@
<script setup> <script setup>
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { getCollection } from './api.js' import { getCollection } from './api.js'
import BookListElement from './BookListElement.vue' import CollectionFormElement from './CollectionFormElement.vue'
import AddBookToCollection from './AddBookToCollection.vue' import AddBookToCollection from './AddBookToCollection.vue'
import Pagination from './Pagination.vue' import Pagination from './Pagination.vue'
@@ -43,7 +43,7 @@ function fetchCollection() {
<h2 class="title">{{ data.name }}</h2> <h2 class="title">{{ data.name }}</h2>
<AddBookToCollection :collection-id="props.id" @created="fetchCollection" /> <AddBookToCollection :collection-id="props.id" @created="fetchCollection" />
<div> <div>
<BookListElement v-for="book in data.books" :key="book.id" v-bind="book" /> <CollectionFormElement v-for="item in data.items" :key="item.id" v-bind="item" />
</div> </div>
<Pagination <Pagination
class="mt-5" class="mt-5"

View File

@@ -0,0 +1,58 @@
<script setup>
import BookListElement from './BookListElement.vue'
const props = defineProps({
position: Number,
book: Array,
})
</script>
<template>
<div class="collectionitembox">
<BookListElement v-bind="props.book">
<div class="separator" />
<div class="positionindicator centered is-narrow">
{{ props.position }}
</div>
<div class="separator" />
<div class="positionwidget centered is-narrow">
<b-icon-list />
</div>
</BookListElement>
</div>
</template>
<style scoped>
.collectionitembox {
transition: ease-in-out 0.04s;
display: flex;
}
.collectionitembox:hover {
transform: scale(1.01);
transition: ease-in-out 0.02s;
}
.separator {
width: 5px;
background: var(--bulma-scheme-main);
}
.positionindicator {
font-size: 36px;
margin-left: 40px;
margin-right: 40px;
}
.positionwidget {
color: var(--bulma-scheme-main);
font-size: 48px;
margin-left: 30px;
margin-right: 30px;
border-top-right-radius: var(--bulma-box-radius);
border-bottom-right-radius: var(--bulma-box-radius);
}
.positionwidget:hover {
cursor: move;
}
</style>

View File

@@ -10,12 +10,37 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
func CollectionQueryToCollectionItemDto(collectionsQueryResult []query.CollectionsQueryResult) []dto.CollectionItemGet { func CollectionItemsQueryToDto(itemsQueryResult []query.CollectionItemQueryResult) []dto.CollectionItemGet {
var collections []dto.CollectionItemGet var dtoItems []dto.CollectionItemGet
for _, queryResult := range itemsQueryResult {
bookItem := dto.BookItemGet{
ID: queryResult.ID,
Title: queryResult.Title,
Author: queryResult.Author,
Description: queryResult.Description,
InventaireID: queryResult.InventaireID,
IsInventaireEdition: queryResult.IsInventaireEdition,
Rating: queryResult.Rating,
Read: queryResult.Read,
StartReadDate: queryResult.StartReadDate,
WantRead: queryResult.WantRead,
CoverPath: queryResult.CoverPath,
}
dtoItems = append(dtoItems,
dto.CollectionItemGet{
Position: queryResult.Position,
Book: bookItem,
})
}
return dtoItems
}
func CollectionQueryToCollectionItemDto(collectionsQueryResult []query.CollectionsQueryResult) []dto.CollectionListItemGet {
var collections []dto.CollectionListItemGet
for _, collectionDb := range collectionsQueryResult { for _, collectionDb := range collectionsQueryResult {
i := findIdInCollection(collections, collectionDb.ID) i := findIdInCollection(collections, collectionDb.ID)
if i == -1 { if i == -1 {
collections = append(collections, dto.CollectionItemGet{ collections = append(collections, dto.CollectionListItemGet{
ID: collectionDb.ID, ID: collectionDb.ID,
Name: collectionDb.Name, Name: collectionDb.Name,
}) })
@@ -30,9 +55,9 @@ func CollectionQueryToCollectionItemDto(collectionsQueryResult []query.Collectio
return collections return collections
} }
func collectionDbToCollectionBookItem(collectionDb *query.CollectionsQueryResult) *dto.CollectionBookItemGet { func collectionDbToCollectionBookItem(collectionDb *query.CollectionsQueryResult) *dto.CollectionListBookItemGet {
if collectionDb.BookId > 0 { if collectionDb.BookId > 0 {
bookItem := dto.CollectionBookItemGet{ bookItem := dto.CollectionListBookItemGet{
ID: collectionDb.BookId, ID: collectionDb.BookId,
Title: collectionDb.BookTitle, Title: collectionDb.BookTitle,
CoverPath: collectionDb.CoverPath, CoverPath: collectionDb.CoverPath,
@@ -44,7 +69,7 @@ func collectionDbToCollectionBookItem(collectionDb *query.CollectionsQueryResult
} }
// returns the position in collections, -1 if not found // returns the position in collections, -1 if not found
func findIdInCollection(collections []dto.CollectionItemGet, collectionId uint) int { func findIdInCollection(collections []dto.CollectionListItemGet, collectionId uint) int {
for i, collection := range collections { for i, collection := range collections {
if collection.ID == collectionId { if collection.ID == collectionId {
return i return i

View File

@@ -13,14 +13,14 @@ func TestGetCollection_Ok(t *testing.T) {
status, collection := testGetCollection(t, "1", "10", "0") status, collection := testGetCollection(t, "1", "10", "0")
assert.Equal(t, http.StatusOK, status) assert.Equal(t, http.StatusOK, status)
assert.Equal(t, "Littérature française", collection.Name) assert.Equal(t, "Littérature française", collection.Name)
assert.Equal(t, 6, len(collection.Books)) assert.Equal(t, 6, len(collection.Items))
} }
func TestGetCollection_Limit(t *testing.T) { func TestGetCollection_Limit(t *testing.T) {
status, collection := testGetCollection(t, "2", "3", "0") status, collection := testGetCollection(t, "2", "3", "0")
assert.Equal(t, http.StatusOK, status) assert.Equal(t, http.StatusOK, status)
assert.Equal(t, "Nouvelles", collection.Name) assert.Equal(t, "Nouvelles", collection.Name)
assert.Equal(t, 3, len(collection.Books)) assert.Equal(t, 3, len(collection.Items))
assert.Equal(t, int64(4), collection.Count) assert.Equal(t, int64(4), collection.Count)
} }
@@ -33,10 +33,17 @@ func TestGetCollection_Empty(t *testing.T) {
status, collection := testGetCollection(t, "4", "10", "0") status, collection := testGetCollection(t, "4", "10", "0")
assert.Equal(t, http.StatusOK, status) assert.Equal(t, http.StatusOK, status)
assert.Equal(t, "Empty", collection.Name) assert.Equal(t, "Empty", collection.Name)
assert.Equal(t, 0, len(collection.Books)) assert.Equal(t, 0, len(collection.Items))
assert.Equal(t, int64(0), collection.Count) assert.Equal(t, int64(0), collection.Count)
} }
func TestGetCollection_Position(t *testing.T) {
status, collection := testGetCollection(t, "2", "3", "0")
assert.Equal(t, http.StatusOK, status)
assert.Equal(t, "Nouvelles", collection.Name)
assert.Equal(t, uint(3), collection.Items[2].Position)
}
func testGetCollection(t *testing.T, id string, limit string, offset string) (int, dto.CollectionGet) { func testGetCollection(t *testing.T, id string, limit string, offset string) (int, dto.CollectionGet) {
return testutils.TestFetchModel[dto.CollectionGet](t, "/ws/collection/"+id, limit, offset) return testutils.TestFetchModel[dto.CollectionGet](t, "/ws/collection/"+id, limit, offset)
} }

View File

@@ -11,24 +11,36 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestPostCollectionBookHandler_Ok(t *testing.T) { func TestPostCollectionBookHandler_AddOk(t *testing.T) {
payload := payload :=
`{ `{
"bookId": 9 "bookId": 9
}` }`
collectionId := "1" collectionId := "1"
status := testPostCollectionBookHandler(t, collectionId, payload) status := testPostCollectionBookHandler(collectionId, payload)
assert.Equal(t, http.StatusOK, status) assert.Equal(t, http.StatusOK, status)
_, collection := testGetCollection(t, collectionId, "10", "0") _, collection := testGetCollection(t, collectionId, "10", "0")
assert.Equal(t, int64(7), collection.Count) assert.Equal(t, int64(7), collection.Count)
} }
func TestPostCollectionBookHandler_BookOK(t *testing.T) {
payload :=
`{
"bookId": 7
}`
collectionId := "2"
status := testPostCollectionBookHandler(collectionId, payload)
assert.Equal(t, http.StatusOK, status)
_, collection := testGetCollection(t, collectionId, "10", "0")
assert.Equal(t, uint(7), collection.Items[0].Book.ID)
}
func TestPostCollectionBookHandler_CollectionNotFound(t *testing.T) { func TestPostCollectionBookHandler_CollectionNotFound(t *testing.T) {
payload := payload :=
`{ `{
"bookId": 9 "bookId": 9
}` }`
status := testPostCollectionBookHandler(t, "12", payload) status := testPostCollectionBookHandler("12", payload)
assert.Equal(t, http.StatusNotFound, status) assert.Equal(t, http.StatusNotFound, status)
} }
@@ -37,7 +49,7 @@ func TestPostCollectionBookHandler_BookNotFound(t *testing.T) {
`{ `{
"bookId": 14654 "bookId": 14654
}` }`
status := testPostCollectionBookHandler(t, "1", payload) status := testPostCollectionBookHandler("1", payload)
assert.Equal(t, http.StatusNotFound, status) assert.Equal(t, http.StatusNotFound, status)
} }
@@ -46,11 +58,11 @@ func TestPostCollectionBookHandler_Unauthorized(t *testing.T) {
`{ `{
"bookId": 9 "bookId": 9
}` }`
status := testPostCollectionBookHandler(t, "3", payload) status := testPostCollectionBookHandler("3", payload)
assert.Equal(t, http.StatusUnauthorized, status) assert.Equal(t, http.StatusUnauthorized, status)
} }
func testPostCollectionBookHandler(t *testing.T, collectionId string, payload string) int { func testPostCollectionBookHandler(collectionId string, payload string) int {
router := testutils.TestSetup() router := testutils.TestSetup()
w := httptest.NewRecorder() w := httptest.NewRecorder()

View File

@@ -23,6 +23,7 @@ func Initdb(databasePath string, demoDataPath string) *gorm.DB {
db.AutoMigrate(&model.UserBook{}) db.AutoMigrate(&model.UserBook{})
db.AutoMigrate(&model.StaticFile{}) db.AutoMigrate(&model.StaticFile{})
db.AutoMigrate(&model.Collection{}) db.AutoMigrate(&model.Collection{})
db.AutoMigrate(&model.CollectionItem{})
var book model.Book var book model.Book
queryResult := db.Limit(1).Find(&book) queryResult := db.Limit(1).Find(&book)
if queryResult.RowsAffected == 0 && demoDataPath != "" { if queryResult.RowsAffected == 0 && demoDataPath != "" {

View File

@@ -47,21 +47,26 @@ type BookItemGet struct {
type CollectionGet struct { type CollectionGet struct {
Name string `json:"name"` Name string `json:"name"`
Count int64 `json:"count"` Count int64 `json:"count"`
Books []BookItemGet `json:"books"` Items []CollectionItemGet `json:"items"`
}
type CollectionItemGet struct {
Position uint `json:"position"`
Book BookItemGet `json:"book"`
} }
type CollectionItemsGet struct { type CollectionItemsGet struct {
Count int64 `json:"count"` Count int64 `json:"count"`
Collections []CollectionItemGet `json:"collections"` Collections []CollectionListItemGet `json:"collections"`
} }
type CollectionItemGet struct { type CollectionListItemGet struct {
ID uint `json:"id"` ID uint `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Books []CollectionBookItemGet `json:"books"` Books []CollectionListBookItemGet `json:"books"`
} }
type CollectionBookItemGet struct { type CollectionListBookItemGet struct {
ID uint `json:"id"` ID uint `json:"id"`
Title string `json:"title"` Title string `json:"title"`
CoverPath string `json:"coverPath"` CoverPath string `json:"coverPath"`

View File

@@ -7,5 +7,5 @@ type Collection struct {
Name string Name string
User User User User
UserID uint UserID uint
Books []Book `gorm:"many2many:collection_books;"` Items []CollectionItem
} }

View File

@@ -0,0 +1,11 @@
package model
import "gorm.io/gorm"
type CollectionItem struct {
gorm.Model
CollectionID uint
Position uint
Book Book
BookID uint
}

View File

@@ -1,7 +1,6 @@
package query package query
import ( import (
"git.artlef.fr/bibliomane/internal/dto"
"git.artlef.fr/bibliomane/internal/model" "git.artlef.fr/bibliomane/internal/model"
"gorm.io/gorm" "gorm.io/gorm"
) )
@@ -64,8 +63,8 @@ func fetchCollectionItemBook(db *gorm.DB, collectionId uint, limit int, offset i
func fetchCollectionItemBooksQuery(db *gorm.DB, collectionId uint) *gorm.DB { func fetchCollectionItemBooksQuery(db *gorm.DB, collectionId uint) *gorm.DB {
query := db.Model(&model.Collection{}) query := db.Model(&model.Collection{})
query = query.Select("collections.id, collections.user_id, collections.name, books.id as book_id, books.title as book_title, " + selectStaticFilesPath()) query = query.Select("collections.id, collections.user_id, collections.name, books.id as book_id, books.title as book_title, " + selectStaticFilesPath())
query = query.Joins("left join collection_books on (collection_books.collection_id = collections.id)") query = query.Joins("left join collection_items on (collection_items.collection_id = collections.id)")
query = query.Joins("left join books on (books.id = collection_books.book_id)") query = query.Joins("left join books on (books.id = collection_items.book_id)")
query = joinStaticFiles(query) query = joinStaticFiles(query)
query = query.Where("collections.id = ?", collectionId) query = query.Where("collections.id = ?", collectionId)
return query return query
@@ -81,14 +80,29 @@ func fetchCollections(db *gorm.DB, userId uint) *gorm.DB {
return db.Model(&model.Collection{}).Where("collections.user_id = ?", userId) return db.Model(&model.Collection{}).Where("collections.user_id = ?", userId)
} }
func FetchCollectionBooks(db *gorm.DB, userId uint, collectionId uint, limit int, offset int) ([]dto.BookItemGet, error) { type CollectionItemQueryResult struct {
var books []dto.BookItemGet Position uint
ID uint
Title string
Author string
Description string
InventaireID string
IsInventaireEdition bool
Rating int
Read bool
StartReadDate string
WantRead bool
CoverPath string
}
func FetchCollectionItems(db *gorm.DB, userId uint, collectionId uint, limit int, offset int) ([]CollectionItemQueryResult, error) {
var collectionitems []CollectionItemQueryResult
query := fetchCollectionBooksQuery(db, userId, collectionId) query := fetchCollectionBooksQuery(db, userId, collectionId)
query = query.Limit(limit) query = query.Limit(limit)
query = query.Offset(offset) query = query.Offset(offset)
query = query.Order("books.id DESC") query = query.Order("collection_items.position")
res := query.Find(&books) res := query.Find(&collectionitems)
return books, res.Error return collectionitems, res.Error
} }
func FetchCollectionBooksCount(db *gorm.DB, userId uint, collectionId uint) (int64, error) { func FetchCollectionBooksCount(db *gorm.DB, userId uint, collectionId uint) (int64, error) {
@@ -98,8 +112,14 @@ func FetchCollectionBooksCount(db *gorm.DB, userId uint, collectionId uint) (int
} }
func fetchCollectionBooksQuery(db *gorm.DB, userId uint, collectionId uint) *gorm.DB { func fetchCollectionBooksQuery(db *gorm.DB, userId uint, collectionId uint) *gorm.DB {
query := fetchBookQueryBuilder(db, userId) query := db.Model(&model.CollectionItem{})
query = query.Joins("left join collection_books on (collection_books.book_id = books.id)") query = query.Select("collection_items.position, " + selectBookItem())
query = query.Where("collection_books.collection_id = ?", collectionId) query = query.Joins("left join collections on (collection_items.collection_id = collections.id)")
query = query.Joins("left join books on (books.id = collection_items.book_id)")
query = joinAuthors(query)
query = query.Joins("left join user_books on (user_books.book_id = books.id and user_books.user_id = ?)", userId)
query = joinStaticFiles(query)
query = query.Order("collection_items.position")
query = query.Where("collections.id = ?", collectionId)
return query return query
} }

View File

@@ -56,7 +56,7 @@ func PostCollectionBookHandler(ac appcontext.AppContext) {
return return
} }
collection.Books = append(collection.Books, book) item := model.CollectionItem{Position: 0, BookID: book.ID, CollectionID: collection.ID}
ac.Db.Save(&collection) ac.Db.Save(&item)
ac.C.String(http.StatusOK, "Success") ac.C.String(http.StatusOK, "Success")
} }

View File

@@ -5,6 +5,7 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"git.artlef.fr/bibliomane/internal/adapter"
"git.artlef.fr/bibliomane/internal/appcontext" "git.artlef.fr/bibliomane/internal/appcontext"
"git.artlef.fr/bibliomane/internal/dto" "git.artlef.fr/bibliomane/internal/dto"
"git.artlef.fr/bibliomane/internal/i18nresource" "git.artlef.fr/bibliomane/internal/i18nresource"
@@ -50,12 +51,13 @@ func GetCollectionHandler(ac appcontext.AppContext) {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err) myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return return
} }
books, err := query.FetchCollectionBooks(ac.Db, user.ID, uint(collectionId), limit, offset) itemsQueryResult, err := query.FetchCollectionItems(ac.Db, user.ID, uint(collectionId), limit, offset)
if err != nil { if err != nil {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err) myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return return
} }
collection.Books = books items := adapter.CollectionItemsQueryToDto(itemsQueryResult)
collection.Items = items
count, err := query.FetchCollectionBooksCount(ac.Db, user.ID, uint(collectionId)) count, err := query.FetchCollectionBooksCount(ac.Db, user.ID, uint(collectionId))
if err != nil { if err != nil {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err) myvalidator.ReturnErrorsAsJsonResponse(&ac, err)