Skip to content
This repository was archived by the owner on Apr 21, 2026. It is now read-only.

Commit 3253d57

Browse files
authored
Merge pull request #24 from paiindustries/feature/blog-filters
fixed blog filtering issues on list page
2 parents 4c62c02 + 4dc013a commit 3253d57

13 files changed

Lines changed: 194 additions & 112 deletions

app/config/routes.cfm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
5555
.get(name = "blog", pattern = "blog", to = "web.BlogController##Index")
5656
.get(name = "blog", pattern = "blog/edit/[id]", to = "web.BlogController##Edit")
57-
.get(name = "blogCategory", pattern = "blog/[filterType]/[filterValue]", to = "web.BlogController##Index")
5857
5958
.get(name = "downloads", pattern = "downloads", to = "web.DownloadsController##Index")
6059
@@ -63,7 +62,8 @@
6362
.get(name = "loadStatuses", pattern = "blog/loadStatuses", to = "web.BlogController##loadStatuses")
6463
.get(name = "loadPostTypes", pattern = "blog/loadPostTypes", to = "web.BlogController##loadPostTypes")
6564
.get(name = "Categories", pattern = "blog/Categories", to = "web.BlogController##Categories")
66-
.get(name = "blogs", pattern = "blog/list/[filterType]/[filterValue]", to = "web.BlogController##blogs")
65+
.get(name = "blogArchive", pattern = "blog/[year]/[month]", to = "web.BlogController##Index")
66+
.get(name = "blogs", pattern = "blog/list/[filterType]/[filterValue]", to = "web.BlogController##Index")
6767
.get(name = "allblogs", pattern = "blog/list", to = "web.BlogController##blogs")
6868
6969
.get(name = "blog-create", pattern = "blog/create", to = "web.BlogController##create")

app/controllers/Controller.cfc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ component extends="wheels.Controller" {
7373
public function getBlogBySlug(required string slug) {
7474
return model("Blog").findOne(
7575
where="blog_posts.slug = '#arguments.slug#'",
76-
include="User, PostStatus"
76+
include="User,PostStatus"
7777
);
7878
}
7979

@@ -83,7 +83,10 @@ component extends="wheels.Controller" {
8383

8484

8585
function getCategoriesByBlogid(required numeric id) {
86-
return model("Category").findAll(where = "blogId = #arguments.id#", include = "Blog");
86+
return model("BlogCategory").findAll(
87+
where = "blogId = #arguments.id#",
88+
include = "Blog,Category"
89+
);
8790
}
8891

8992
function getAttachmentsByBlogid(required numeric id) {

app/controllers/web/BlogController.cfc

Lines changed: 69 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,68 @@ component extends="app.Controllers.Controller" {
99
}
1010

1111
// Function to list all blogs
12-
function index() {
12+
public void function index() {
13+
// Detect HTMX request
14+
// var isHtmx = structKeyExists(getHttpRequestData().headers, "HX-Request");
15+
16+
// Detect filters via category/tag or year/month
17+
var isFiltered = (
18+
structKeyExists(params, "year") && structKeyExists(params, "month")
19+
);
20+
21+
// If filtered or HTMX, just call the blogs partial and return
22+
if (isFiltered) {
23+
blogs();
24+
return;
25+
}
26+
27+
// Otherwise, allow default layout-wrapped view to render (index.cfm)
1328
}
1429

15-
// load blog list
16-
function blogs() {
17-
var blogModel = model("Blog"); // Get model instance
1830

19-
if(isDefined("params.filterType") and params.filterType == 'category') {
20-
blogs = getAllByCategory(params.filterValue);
21-
} else if(isDefined("params.filterType") and params.filterType == 'tag') {
31+
public void function blogs() {
32+
var blogModel = model("Blog"); // Load model
33+
34+
// --- Filter by category or tag ---
35+
if (structKeyExists(params, "filterType") && structKeyExists(params, "filterValue")) {
36+
// Normalize value (e.g. convert "design-ui" to "design.ui")
37+
params.filterValue = replace(params.filterValue, "-", ".", "all");
38+
39+
switch (lcase(params.filterType)) {
40+
case "category":
41+
blogs = getBlogsByCategory(params.filterValue);
42+
break;
43+
case "tag":
2244
blogs = getAllByTag(params.filterValue);
23-
} else if(isDefined("params.filterType") and params.filterType == 'monthyear') {
24-
blogs = getAllByMonthYear(params.filterValue);
45+
break;
46+
default:
47+
blogs = getAllBlogs(); // fallback in case of unknown filterType
48+
}
49+
50+
// --- Filter by archive year/month ---
51+
} else if (structKeyExists(params, "year") && structKeyExists(params, "month")) {
52+
if (isNumeric(params.year) && isNumeric(params.month)) {
53+
blogs = getBlogsByMonthYear(params.year, params.month);
54+
} else {
55+
blogs = getAllBlogs(); // fallback in case of invalid input
56+
}
57+
58+
// --- Default blog listing ---
2559
} else {
2660
blogs = getAllBlogs();
2761
}
28-
renderPartial(partial="partials/blogList", locals={blogs: blogs});
62+
63+
renderPartial(partial="partials/blogList");
2964
}
3065

3166
// Function to load categories for the blog list
3267
function categories() {
33-
categorylist = model("BlogCategory").getAll();
68+
categorylist = model("Category").getAll();
3469
renderPartial(partial="partials/categorylist");
3570
}
3671

3772
// Function to show the create blog form
3873
function create() {
39-
// writeDump(cgi.path_info & "?" & cgi.query_string); abort;
4074
saveRedirectUrl(cgi.script_name & "?" & cgi.query_string);
4175
renderView(layout="blogLayout");
4276
}
@@ -134,7 +168,7 @@ component extends="app.Controllers.Controller" {
134168

135169
// Function to load categories for the dropdown
136170
function loadCategories() {
137-
categories = model("BlogCategory").getAll();
171+
categories = model("Category").getAll();
138172
renderPartial(partial="partials/categories");
139173
}
140174

@@ -169,32 +203,39 @@ component extends="app.Controllers.Controller" {
169203
);
170204
}
171205

172-
private function getAllByMonthYear(required numeric monthyear){
173-
// Convert monthyear to string for easier manipulation
174-
var strMonthYear = LCase(trim(monthyear));
175-
176-
// Extract month (first 2 digits) and year (remaining digits)
177-
var month = Left(strMonthYear, 2);
178-
var year = Right(strMonthYear, Len(strMonthYear) - 2);
179-
206+
private function getBlogsByMonthYear(required numeric year, required string month) {
180207
// Create start and end date for the selected month
181208
var startdate = "#year#-#NumberFormat(month, '00')#-01 00:00:00";
182-
var enddate = "#year#-#NumberFormat(month, '00')#-#DaysInMonth('#year#-#NumberFormat(month, '00')#-01')# 23:59:59";
209+
var enddate = "#year#-#NumberFormat(month, '00')#-#DaysInMonth('#year#-#NumberFormat(month, '00')#-01')# 23:59:59";
183210

184211
return model("Blog").findAll(
185-
where="blog_posts.createdAt BETWEEN '#startdate#' AND '#enddate#'",
186-
order="createdAt DESC",
212+
where="blog_posts.post_created_date BETWEEN '#startdate#' AND '#enddate#'",
213+
order="createdat DESC",
187214
include="User",
188215
returnAs="query"
189216
);
190217
}
191218

192219
// Fetch Blogs by Category
193-
private function getAllByCategory(required string category){
220+
public function getBlogsByCategory(required string categoryName) {
221+
// Get category ID from name
222+
var category = model("Category").findOne(where="name = '#arguments.categoryName#'");
223+
if (!isObject(category)) return [];
224+
225+
// Get all blog-category mappings with that category
226+
var blogCategoryQuery = model("BlogCategory")
227+
.findAll(where="categoryId = #category.id#", returnAs="query");
228+
229+
if (blogCategoryQuery.recordCount == 0) return [];
230+
231+
// Extract blogIds
232+
var blogIds = blogCategoryQuery.columnData("blogId");
233+
234+
// Get blog posts with matching IDs
194235
return model("Blog").findAll(
195-
where="categoryName = '#category#'",
236+
where="id IN (#arrayToList(blogIds)#)",
196237
order="createdAt DESC",
197-
include="User,category",
238+
include="User,BlogCategory",
198239
returnAs="query"
199240
);
200241
}
@@ -431,7 +472,7 @@ component extends="app.Controllers.Controller" {
431472

432473
// Insert new categories
433474
for (var category_Id in categoryArray) {
434-
var newCategory = model("Category").new();
475+
var newCategory = model("BlogCategory").new();
435476
newCategory.categoryName = category_Id;
436477
newCategory.blogId = blogId;
437478
newCategory.createdAt = now();

app/migrator/migrations/20250228153551_creates_category_table.cfc renamed to app/migrator/migrations/20250228153551_creates_blog_category_table.cfc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ component extends="wheels.migrator.Migration" hint="creates category table" {
44
var local = {};
55
try {
66
// create categories table
7-
var t = createTable(name='categories', force=false, id=true, primaryKey='id');
7+
var t = createTable(name='blog_categories', force=false, id=true, primaryKey='id');
88
t.string(columnNames='category_id', null=false, default='', limit=255);
99
t.timestamps();
1010
t.create();
@@ -27,7 +27,7 @@ component extends="wheels.migrator.Migration" hint="creates category table" {
2727
var local = {};
2828
try {
2929
// drop categories table
30-
dropTable(name='categories');
30+
dropTable(name='blog_categories');
3131
transaction action="commit";
3232
} catch (any e) {
3333
local.exception = e;

app/migrator/migrations/20250305143040_creates_blog_category_table.cfc renamed to app/migrator/migrations/20250305143040_creates_category_table.cfc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
component extends="wheels.migrator.Migration" hint="creates blog_category table" {
1+
component extends="wheels.migrator.Migration" hint="creates category table" {
22

33
function up() {
44
transaction {
55
try {
6-
// create blog_categories table
7-
t = createTable(name = 'blog_categories', force=false, id=true, primaryKey='id');
6+
// create categories table
7+
t = createTable(name = 'categories', force=false, id=true, primaryKey='id');
88
t.string(columnNames='name', null=false, default='', limit=255);
99
t.string(columnNames='description', null=true, default='', limit=500);
1010
t.integer(columnNames='parent_id', null=true);
@@ -27,8 +27,8 @@ component extends="wheels.migrator.Migration" hint="creates blog_category table"
2727
function down() {
2828
transaction {
2929
try {
30-
// drop blog_categories table
31-
dropTable(name = 'blog_categories');
30+
// drop categories table
31+
dropTable(name = 'categories');
3232
} catch (any e) {
3333
local.exception = e;
3434
}

app/migrator/migrations/20250306112302_insert_records.cfc

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,20 @@ component extends="wheels.migrator.Migration" hint="insert records" {
99
addRecord(table='Roles',name = "user");
1010

1111
// users
12-
addRecord(table="users", first_name="Peter", last_name="Amiri", email="peteramiri@gmail.com", password_hash="$2a$10$P27CV/m.aramHhIxJTmzzu4dxIGfNqHWzLgVGJJTLDpXymnt4jPZu", profile_picture='', profile_url='', status=1, role_id=1);
12+
addRecord(table="users", first_name="Peter", last_name="Amiri", email="petera@pai.com", password_hash="$2a$10$P27CV/m.aramHhIxJTmzzu4dxIGfNqHWzLgVGJJTLDpXymnt4jPZu", profile_picture='/images/avatar-rounded.webp', profile_url='', status=1, role_id=1);
1313

14-
// blog_categories
15-
addRecord(table='blog_categories', name='CLI', parent_id='', description='Learn about command-line tools, tips, and tricks for enhancing your development workflow using the command line.');
16-
addRecord(table='blog_categories', name='Community', parent_id='', description='Updates and stories from the community, including user spotlights, testimonials, and community-driven initiatives.');
17-
addRecord(table='blog_categories', name='Contributions', parent_id='', description='Information on contributing to open-source projects, including guidelines, best practices, and featured contributors.');
18-
addRecord(table='blog_categories', name='Documentation', parent_id='', description='Guides, updates, and tips on how to use and contribute to documentation effectively.');
19-
addRecord(table='blog_categories', name='Events', parent_id='', description='Announcements and recaps of conferences, webinars, meetups, and other events related to development and technology.');
20-
addRecord(table='blog_categories', name='Inspiration', parent_id='', description='Success stories, case studies, and creative ideas to inspire your next project or solution.');
21-
addRecord(table='blog_categories', name='Plugin', parent_id='', description='Reviews, tutorials, and updates about plugins that extend the functionality of the platform or framework.');
22-
addRecord(table='blog_categories', name='Releases', parent_id='', description='Detailed information on new releases, version updates, changelogs, and feature highlights.');
23-
addRecord(table='blog_categories', name='Tips & Tricks', parent_id='', description='Quick and effective tips to enhance productivity and solve common development challenges.');
24-
addRecord(table='blog_categories', name='Tutorials', parent_id='', description='Step-by-step guides and how-tos for beginners and advanced users alike, covering a wide range of topics.');
25-
addRecord(table='blog_categories', name='Website', parent_id='', description='News, updates, and improvements related to the website, including UI/UX enhancements and new features.');
14+
// categories
15+
addRecord(table='categories', name='CLI', parent_id='', description='Learn about command-line tools, tips, and tricks for enhancing your development workflow using the command line.');
16+
addRecord(table='categories', name='Community', parent_id='', description='Updates and stories from the community, including user spotlights, testimonials, and community-driven initiatives.');
17+
addRecord(table='categories', name='Contributions', parent_id='', description='Information on contributing to open-source projects, including guidelines, best practices, and featured contributors.');
18+
addRecord(table='categories', name='Documentation', parent_id='', description='Guides, updates, and tips on how to use and contribute to documentation effectively.');
19+
addRecord(table='categories', name='Events', parent_id='', description='Announcements and recaps of conferences, webinars, meetups, and other events related to development and technology.');
20+
addRecord(table='categories', name='Inspiration', parent_id='', description='Success stories, case studies, and creative ideas to inspire your next project or solution.');
21+
addRecord(table='categories', name='Plugin', parent_id='', description='Reviews, tutorials, and updates about plugins that extend the functionality of the platform or framework.');
22+
addRecord(table='categories', name='Releases', parent_id='', description='Detailed information on new releases, version updates, changelogs, and feature highlights.');
23+
addRecord(table='categories', name='Tips & Tricks', parent_id='', description='Quick and effective tips to enhance productivity and solve common development challenges.');
24+
addRecord(table='categories', name='Tutorials', parent_id='', description='Step-by-step guides and how-tos for beginners and advanced users alike, covering a wide range of topics.');
25+
addRecord(table='categories', name='Website', parent_id='', description='News, updates, and improvements related to the website, including UI/UX enhancements and new features.');
2626

2727
// post_types
2828
addRecord(table='post_types', name='Standard Post', description='Regular blog post with a title, content, and optional featured image.', is_active=1);

app/migrator/migrations/20250319093914_create_blog_id_column.cfc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@
1616
EXAMPLE:
1717
addColumn(table='members', columnType='string', columnName='status', limit=50);
1818
*/
19-
component extends="wheels.migrator.Migration" hint="create_blog_id_column" {
19+
component extends="wheels.migrator.Migration" hint="create_blog_id_column_in_blog_categories" {
2020

2121
function up() {
2222
transaction {
2323
try {
24-
addColumn(table = 'categories', columnType = 'integer', columnName = 'blog_id', default = '', null = true);
24+
addColumn(table = 'blog_categories', columnType = 'integer', columnName = 'blog_id', default = '', null = true);
2525

2626
// add foreign key constraint
2727
addForeignKey(
28-
table='categories',
28+
table='blog_categories',
2929
column='blog_id',
3030
referenceTable='blog_posts',
3131
referenceColumn='id'
@@ -48,9 +48,9 @@ component extends="wheels.migrator.Migration" hint="create_blog_id_column" {
4848
transaction {
4949
try {
5050
// drop foreign key constraints using raw SQL
51-
execute(sql="ALTER TABLE categories DROP CONSTRAINT IF EXISTS fk_categories_blog_id");
51+
execute(sql="ALTER TABLE blog_categories DROP CONSTRAINT IF EXISTS fk_categories_blog_id");
5252

53-
removeColumn(table = 'categories', columnName = 'blog_id');
53+
removeColumn(table = 'blog_categories', columnName = 'blog_id');
5454
} catch (any e) {
5555
local.exception = e;
5656
}

app/models/Blog.cfc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,12 @@ component extends="app.Models.Model" {
2626
property(name="createdBy", column="created_by", type="integer", required=true, foreignkey=true, references="User(id)");
2727
property(name="updatedBy", column="updated_by", type="integer", required=false, foreignkey=true, references="User(id)");
2828
property(name="deletedBy", column="deleted_by", type="integer", required=false, foreignkey=true, references="User(id)");
29-
property(name="tagId", column="blog_id", type="integer", required=false, foreignkey=true, references="Tag(id)");
3029

3130
// Define associations
3231
belongsTo(name="User", foreignKey="createdBy");
3332
belongsTo(name="PostStatus", foreignKey="statusId");
3433
belongsTo(name="PostType", foreignKey="postTypeId");
35-
hasmany(name="category", foreignKey="blogId");
34+
hasMany(name="BlogCategory", foreignKey="blogId");
3635
hasmany(name="tag", foreignKey="blogId");
3736

3837
}

app/models/BlogCategory.cfc

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,21 @@ component extends="app.Models.Model" {
33
table("blog_categories");
44

55
property(name="id", column="id", type="integer", required=true, primarykey=true);
6-
property(name="name", column="name", type="string", required=false, default="");
7-
property(name="parentId", column="parent_id", type="string", required=false, default="");
8-
property(name="description", column="description", type="text", required=false, default="");
9-
property(name="isActive", column="is_active", type="boolean", required=false, default="");
6+
property(name="categoryId", column="category_id", type="integer", required=false, default="");
107
property(name="createdAt", column="createdat", type="datetime", required=false, default="");
118
property(name="updatedAt", column="updatedat", type="datetime", required=false, default="");
129
property(name="deletedAt", column="deletedat", type="datetime", required=false, default="");
10+
11+
property(name="blogId", column="blog_id", type="integer", required=false);
12+
13+
// Associations
14+
belongsTo(name="Blog", foreignKey="blogId");
15+
belongsTo(name="Category", foreignKey="categoryId");
1316
}
1417

15-
// fetch all blog_categories
18+
// fetch all blog categories with category name
1619
public function getAll(){
17-
var categories = findAll();
20+
var categories = findAll(include="Category"); // include associated Category
1821
return categories;
1922
}
2023
}

0 commit comments

Comments
 (0)