Skip to content

Commit b7fe1da

Browse files
committed
/series/add: add support for showing sub-categories.
Addressed to #434
1 parent ade1ede commit b7fe1da

File tree

13 files changed

+310
-10
lines changed

13 files changed

+310
-10
lines changed

src/main/java/ru/mystamps/web/controller/SeriesController.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
import ru.mystamps.web.service.SeriesSalesService;
7474
import ru.mystamps.web.service.SeriesService;
7575
import ru.mystamps.web.service.TransactionParticipantService;
76+
import ru.mystamps.web.service.dto.FirstLevelCategoryDto;
7677
import ru.mystamps.web.service.dto.SeriesDto;
7778
import ru.mystamps.web.support.spring.security.Authority;
7879
import ru.mystamps.web.support.spring.security.CustomUserDetails;
@@ -123,9 +124,9 @@ public Map<Integer, Integer> getYears() {
123124
}
124125

125126
@ModelAttribute("categories")
126-
public List<LinkEntityDto> getCategories(Locale userLocale) {
127+
public List<FirstLevelCategoryDto> getCategories(Locale userLocale) {
127128
String lang = LocaleUtils.getLanguageOrNull(userLocale);
128-
return categoryService.findAllAsLinkEntities(lang);
129+
return categoryService.findFirstLevelCategories(lang);
129130
}
130131

131132
@ModelAttribute("countries")

src/main/java/ru/mystamps/web/dao/CategoryDao.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
import java.util.List;
2222

2323
import ru.mystamps.web.dao.dto.AddCategoryDbDto;
24+
import ru.mystamps.web.dao.dto.CategoryDto;
2425
import ru.mystamps.web.dao.dto.LinkEntityDto;
2526

27+
@SuppressWarnings("PMD.TooManyMethods")
2628
public interface CategoryDao {
2729
Integer add(AddCategoryDbDto category);
2830
long countAll();
@@ -35,4 +37,5 @@ public interface CategoryDao {
3537
List<Object[]> getStatisticsOf(Integer collectionId, String lang);
3638
List<LinkEntityDto> findAllAsLinkEntities(String lang);
3739
LinkEntityDto findOneAsLinkEntity(String slug, String lang);
40+
List<CategoryDto> findCategoriesWithParents(String lang);
3841
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright (C) 2009-2017 Slava Semushin <slava.semushin@gmail.com>
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17+
*/
18+
package ru.mystamps.web.dao.dto;
19+
20+
import lombok.Getter;
21+
import lombok.RequiredArgsConstructor;
22+
import lombok.ToString;
23+
24+
@Getter
25+
@ToString
26+
@RequiredArgsConstructor
27+
public class CategoryDto {
28+
private final String name;
29+
private final String slug;
30+
private final String parentName;
31+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright (C) 2009-2017 Slava Semushin <slava.semushin@gmail.com>
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17+
*/
18+
package ru.mystamps.web.dao.dto;
19+
20+
import lombok.Getter;
21+
import lombok.RequiredArgsConstructor;
22+
import lombok.ToString;
23+
24+
@Getter
25+
@ToString
26+
@RequiredArgsConstructor
27+
public class EntityWithSlugDto {
28+
private final String name;
29+
private final String slug;
30+
}

src/main/java/ru/mystamps/web/dao/impl/JdbcCategoryDao.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,11 @@
3636

3737
import ru.mystamps.web.dao.CategoryDao;
3838
import ru.mystamps.web.dao.dto.AddCategoryDbDto;
39+
import ru.mystamps.web.dao.dto.CategoryDto;
3940
import ru.mystamps.web.dao.dto.LinkEntityDto;
4041

4142
@RequiredArgsConstructor
42-
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
43+
@SuppressWarnings({ "PMD.AvoidDuplicateLiterals", "PMD.TooManyMethods" })
4344
public class JdbcCategoryDao implements CategoryDao {
4445

4546
private final NamedParameterJdbcTemplate jdbcTemplate;
@@ -77,6 +78,10 @@ public class JdbcCategoryDao implements CategoryDao {
7778
@Value("${category.find_category_link_info_by_slug}")
7879
private String findLinkEntityBySlugSql;
7980

81+
@SuppressWarnings("PMD.LongVariable")
82+
@Value("${category.find_categories_with_parent_names}")
83+
private String findCategoriesWithParentNamesSql;
84+
8085
@Override
8186
public Integer add(AddCategoryDbDto category) {
8287
Map<String, Object> params = new HashMap<>();
@@ -207,4 +212,13 @@ public LinkEntityDto findOneAsLinkEntity(String slug, String lang) {
207212
}
208213
}
209214

215+
@Override
216+
public List<CategoryDto> findCategoriesWithParents(String lang) {
217+
return jdbcTemplate.query(
218+
findCategoriesWithParentNamesSql,
219+
Collections.singletonMap("lang", lang),
220+
RowMappers::forCategoryDto
221+
);
222+
}
223+
210224
}

src/main/java/ru/mystamps/web/dao/impl/RowMappers.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,4 +253,12 @@ public static EntityWithIdDto forEntityWithIdDto(ResultSet rs, int i) throws SQL
253253
);
254254
}
255255

256+
public static CategoryDto forCategoryDto(ResultSet rs, int i) throws SQLException {
257+
return new CategoryDto(
258+
rs.getString("name"),
259+
rs.getString("slug"),
260+
rs.getString("parent_name")
261+
);
262+
}
263+
256264
}

src/main/java/ru/mystamps/web/service/CategoryService.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@
2222

2323
import ru.mystamps.web.dao.dto.LinkEntityDto;
2424
import ru.mystamps.web.service.dto.AddCategoryDto;
25+
import ru.mystamps.web.service.dto.FirstLevelCategoryDto;
2526

27+
@SuppressWarnings("PMD.TooManyMethods")
2628
public interface CategoryService {
2729
String add(AddCategoryDto dto, Integer userId);
2830
List<LinkEntityDto> findAllAsLinkEntities(String lang);
31+
List<FirstLevelCategoryDto> findFirstLevelCategories(String lang);
2932
LinkEntityDto findOneAsLinkEntity(String slug, String lang);
3033
long countAll();
3134
long countCategoriesOf(Integer collectionId);

src/main/java/ru/mystamps/web/service/CategoryServiceImpl.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
*/
1818
package ru.mystamps.web.service;
1919

20+
import java.util.ArrayList;
21+
import java.util.Collections;
2022
import java.util.Date;
2123
import java.util.List;
2224
import java.util.Locale;
@@ -35,12 +37,16 @@
3537

3638
import ru.mystamps.web.dao.CategoryDao;
3739
import ru.mystamps.web.dao.dto.AddCategoryDbDto;
40+
import ru.mystamps.web.dao.dto.CategoryDto;
41+
import ru.mystamps.web.dao.dto.EntityWithSlugDto;
3842
import ru.mystamps.web.dao.dto.LinkEntityDto;
3943
import ru.mystamps.web.service.dto.AddCategoryDto;
44+
import ru.mystamps.web.service.dto.FirstLevelCategoryDto;
4045
import ru.mystamps.web.support.spring.security.HasAuthority;
4146
import ru.mystamps.web.util.LocaleUtils;
4247
import ru.mystamps.web.util.SlugUtils;
4348

49+
@SuppressWarnings("PMD.TooManyMethods")
4450
@RequiredArgsConstructor
4551
public class CategoryServiceImpl implements CategoryService {
4652
private static final Logger LOG = LoggerFactory.getLogger(CategoryServiceImpl.class);
@@ -85,6 +91,47 @@ public List<LinkEntityDto> findAllAsLinkEntities(String lang) {
8591
return categoryDao.findAllAsLinkEntities(lang);
8692
}
8793

94+
@Override
95+
@Transactional(readOnly = true)
96+
@PreAuthorize(HasAuthority.CREATE_SERIES)
97+
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
98+
public List<FirstLevelCategoryDto> findFirstLevelCategories(String lang) {
99+
List<CategoryDto> categories = categoryDao.findCategoriesWithParents(lang);
100+
if (categories.isEmpty()) {
101+
return Collections.emptyList();
102+
}
103+
104+
// Because of Thymeleaf's restrictions we can't return categories as-is and need this
105+
// transformation
106+
List<FirstLevelCategoryDto> items = new ArrayList<>();
107+
String lastParent = null;
108+
FirstLevelCategoryDto lastItem = null;
109+
110+
for (CategoryDto category : categories) {
111+
String name = category.getName();
112+
String slug = category.getSlug();
113+
String parent = category.getParentName();
114+
115+
boolean categoryWithoutParent = parent == null;
116+
boolean createNewItem = categoryWithoutParent || !parent.equals(lastParent);
117+
118+
if (createNewItem) {
119+
lastParent = parent;
120+
if (categoryWithoutParent) {
121+
lastItem = new FirstLevelCategoryDto(name, slug);
122+
} else {
123+
lastItem = new FirstLevelCategoryDto(parent);
124+
lastItem.getChildren().add(new EntityWithSlugDto(name, slug));
125+
}
126+
items.add(lastItem);
127+
} else {
128+
lastItem.getChildren().add(new EntityWithSlugDto(name, slug));
129+
}
130+
}
131+
132+
return items;
133+
}
134+
88135
@Override
89136
@Transactional(readOnly = true)
90137
public LinkEntityDto findOneAsLinkEntity(String slug, String lang) {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (C) 2009-2017 Slava Semushin <slava.semushin@gmail.com>
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17+
*/
18+
package ru.mystamps.web.service.dto;
19+
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
23+
import lombok.Getter;
24+
import lombok.ToString;
25+
26+
import ru.mystamps.web.dao.dto.EntityWithSlugDto;
27+
28+
@Getter
29+
@ToString
30+
public class FirstLevelCategoryDto {
31+
private final String name;
32+
private final String slug;
33+
private final List<EntityWithSlugDto> children = new ArrayList<>();
34+
35+
public FirstLevelCategoryDto(String name, String slug) {
36+
this.name = name;
37+
this.slug = slug;
38+
}
39+
40+
public FirstLevelCategoryDto(String name) {
41+
this(name, null);
42+
}
43+
44+
}

src/main/resources/liquibase/version/0.4.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@
2323
<include file="0.4/2016-10-10--change_series_sales_date_type.xml" relativeToChangelogFile="true" />
2424
<include file="0.4/2016-11-29--add-unique-key-transaction_participants-table.xml" relativeToChangelogFile="true" />
2525
<include file="0.4/2016-12-05--make_name_ru_optional.xml" relativeToChangelogFile="true" />
26+
<include file="0.4/2017-01-06--top_categories.xml" relativeToChangelogFile="true" />
2627

2728
</databaseChangeLog>
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<databaseChangeLog
3+
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
6+
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
7+
8+
<changeSet id="create-top_categories-table" author="php-coder" context="scheme">
9+
10+
<createTable tableName="top_categories">
11+
<column name="id" type="INTEGER" autoIncrement="true">
12+
<constraints primaryKey="true" />
13+
</column>
14+
<column name="name" type="VARCHAR(50)">
15+
<constraints unique="true" uniqueConstraintName="uc_top_categories_name" nullable="false" />
16+
</column>
17+
<column name="name_ru" type="VARCHAR(50)">
18+
<constraints unique="true" uniqueConstraintName="uc_top_categories_name_ru" />
19+
</column>
20+
<column name="created_at" type="DATETIME">
21+
<constraints nullable="false" />
22+
</column>
23+
<column name="created_by" type="INTEGER">
24+
<constraints nullable="false" references="users(id)" foreignKeyName="fk_top_categories_created_by" />
25+
</column>
26+
<column name="updated_at" type="DATETIME">
27+
<constraints nullable="false" />
28+
</column>
29+
<column name="updated_by" type="INTEGER">
30+
<constraints nullable="false" references="users(id)" foreignKeyName="fk_top_categories_updated_by" />
31+
</column>
32+
</createTable>
33+
34+
</changeSet>
35+
36+
<changeSet id="add-categories-top_category_id-field" author="php-coder" context="scheme">
37+
38+
<addColumn tableName="categories">
39+
<column name="top_category_id" type="INTEGER" beforeColumn="created_at">
40+
<constraints references="top_categories(id)" foreignKeyName="fk_top_categories_id" />
41+
</column>
42+
</addColumn>
43+
44+
</changeSet>
45+
46+
<changeSet id="add-fauna-top-level-category" author="php-coder" context="test-data, prod-data">
47+
48+
<insert tableName="top_categories">
49+
<column name="name" value="Fauna" />
50+
<column name="name_ru" value="Фауна" />
51+
<column name="created_at" valueComputed="${NOW}" />
52+
<column name="created_by" valueComputed="(SELECT id FROM users WHERE role = 'ADMIN' ORDER by id LIMIT 1)" />
53+
<column name="updated_at" valueComputed="${NOW}" />
54+
<column name="updated_by" valueComputed="(SELECT id FROM users WHERE role = 'ADMIN' ORDER by id LIMIT 1)" />
55+
</insert>
56+
57+
</changeSet>
58+
59+
<changeSet id="move-prehistoric-animals-inside-fauna" author="php-coder" context="test-data, prod-data">
60+
61+
<update tableName="categories">
62+
<column name="top_category_id" valueComputed="(SELECT id FROM top_categories WHERE name = 'Fauna')" />
63+
<where>slug = 'prehistoric-animals'</where>
64+
</update>
65+
66+
</changeSet>
67+
68+
<changeSet id="delete-animals-category" author="php-coder" context="test-data">
69+
70+
<delete tableName="categories">
71+
<where>slug = 'animals'</where>
72+
</delete>
73+
74+
</changeSet>
75+
76+
<changeSet id="add-sport-category" author="php-coder" context="test-data">
77+
78+
<insert tableName="categories">
79+
<column name="name" value="Sport" />
80+
<column name="name_ru" value="Спорт" />
81+
<column name="slug" value="sport" />
82+
<column name="created_at" valueComputed="${NOW}" />
83+
<column name="created_by" valueComputed="(SELECT id FROM users WHERE role = 'ADMIN' ORDER by id LIMIT 1)" />
84+
<column name="updated_at" valueComputed="${NOW}" />
85+
<column name="updated_by" valueComputed="(SELECT id FROM users WHERE role = 'ADMIN' ORDER by id LIMIT 1)" />
86+
</insert>
87+
88+
</changeSet>
89+
90+
</databaseChangeLog>

0 commit comments

Comments
 (0)