Честно я не работал с YII, но сделал бы наверное так.
таблица urls в это таблице храним слаги урлов, причем только тот который является последним сегментом в адресе, тоесть если адрес site.ru/категория1/категория2/ такой то в слаг будет записано "каегория2"
Таблица будет содержать в себе столбцы id, pageable_id, pageable_type, slud
Полиморфная связь
pageable_type - короткий идентификатор объекта или его полный namespace путь
pageable_id - это id продукта, категории, страницы и чего угодно что нужно вам обработать.
Создаем контроллер который обрабатывает все подряд адреса, кроме тех, которые прописаны в роутах
Контроллер получает полный URI и берет его последний сегмент, ищет в моделе Url по слагу, если не найдено ничего 404 иначе работаем дальше.
Категории нужно сделать через nestedset, итак у нас есть категория по последнему сегменту, получаем всех родителей и строим новый URI сравниваем с тем по которому мы сейчас находимся, если не совпадает то редиректим на тот что мы сгенерировали сами, это нужно чтобы не было доступа к одной и той же странице по разным адресам.
но предупрежу сразу такая схема будет работать только тогда когда у категорий может только один родитель
Иначе лучше сделать без вложености