== Напреден апликативен развој Имплементирани се сите случаи на употреба дефинирани во фаза 3. === Примери за индекси Индексите се користат за забрзување на пребарувања што често се изведуваат. Во Travel Sage, се прави пребарување на активности според период, филтрирање на резервации по корисник, како и пребарување на дестинации според име. Со додавање индекси на овие колони, апликацијата работи побрзо и поефикасно. * За побрзо пребарување на активности во одреден период {{{ CREATE INDEX idx_aktivnost_datum ON aktivnosti(datum_od, datum_do); }}} * За побрз join и филтрирање по корисник при резервации {{{ CREATE INDEX idx_rezervacii_korisnik ON rezervacii(idkorisnik); }}} * За побрзо пребарување на дестинации по име на локација {{{ CREATE INDEX idx_destinacii_lokacija ON destinacii(imelokacija); }}} === Примери за тригери Тригерите се користат за автоматска реакција на промена во базата. Во TravelSage, тие се применети за: контрола на интегритет (нема дупли препораки) и автоматска реакција на негативна рецензија. 1. Спречување дупли препораки Ако корисник веќе има препорака за истата дестинација, не му се дозволува повторно да ја додаде. {{{ CREATE OR REPLACE FUNCTION prevent_duplicate_recommendation() RETURNS TRIGGER AS $$ BEGIN IF EXISTS ( SELECT 1 FROM preporaki WHERE idkorisnik = NEW.idkorisnik AND iddestinacija = NEW.iddestinacija ) THEN RAISE EXCEPTION 'User already has this destination in recommendations!'; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trg_check_duplicate_recommendation BEFORE INSERT ON preporaki FOR EACH ROW EXECUTE FUNCTION prevent_duplicate_recommendation(); }}} 2. Деактивирање дестинација со негативни рецензии Ако некој внесе рецензија со оцена помала од 3, системот автоматски ја прави дестинацијата неактивна за да не се прикажува во понатамошните резултати. {{{ CREATE OR REPLACE FUNCTION check_negative_review() RETURNS TRIGGER AS $$ BEGIN IF NEW.ocenka < 3 THEN UPDATE destinacii SET aktivna = FALSE WHERE iddest = NEW.iddest; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trg_negative_review AFTER INSERT ON recenzii FOR EACH ROW EXECUTE FUNCTION check_negative_review(); }}} === Примери за прегледи View се користи за да се поедностават комплексни пребарувања. 1. Во TravelSage, прикажани се активности што се „најдобра вредност за пари“, базирано на цена и просечна оцена од рецензии. {{{ CREATE VIEW best_value_activities AS SELECT a.idaktivnost, a.ime, a.iznos, d.imelokacija, AVG(r.ocenka) AS avg_rating FROM aktivnosti a JOIN destinacii d ON a.iddest = d.iddest JOIN recenzii r ON a.idaktivnost = r.idaktivnost GROUP BY a.idaktivnost, a.ime, a.iznos, d.imelokacija HAVING AVG(r.ocenka) > 4 AND a.iznos < 1000; }}} 2. Активности со најниски цени Го пресметува процентот на ефтини активности по дестинација – корисно за филтрирање при буџетско патување. {{{ DB::statement(" CREATE VIEW view_procent_cheap_destinations AS SELECT d.imelokacija, COUNT(CASE WHEN a.iznos < 500 THEN 1 END) * 100.0 / COUNT(*) AS procent_cheap FROM travel_sage.destinacii d JOIN travel_sage.aktivnosti a ON d.iddest = a.iddest GROUP BY d.iddest, d.imelokacija ORDER BY procent_cheap DESC "); }}} === Трансакции 1. Пример при резервација на активност Ако не успее било кој дел (резервација или ажурирање на квота), не се зачувува ништо – атомичност. {{{ DB::transaction(function () use ($request) { DB::table('rezervacii')->insert([ 'idkorisnik' => auth()->id(), 'idaktivnost' => $request->aktivnost_id, 'datum' => now() ]); DB::table('aktivnosti') ->where('idaktivnost', $request->aktivnost_id) ->decrement('kvota', 1); }); }}} 2. Внесување на нови податоци во табелите за пакети, настани и активности Се користи трансакција за да сигурност дека внесувањето е валидно и се запишува како целина. {{{ DB::transaction(function () use ($request) { $validatedData = $request->validate([ 'imepaket' => 'required|string|max:255', 'cena' => 'required|numeric', 'pochetok' => 'required|date_format:Y-m-d\TH:i', 'kraj' => 'required|date_format:Y-m-d\TH:i|after_or_equal:pochetok', ]); TravelPackage::create($validatedData); }); }}} {{{ DB::transaction(function () use ($request) { $validatedData = $request->validate([ 'naziv' => 'required|string|max:255', 'vidovi' => 'required|string|max:255', 'detali' => 'nullable|string', 'pochetendatum' => 'required|date', 'kraendatum' => 'required|date', ]); TravelEvent::create($validatedData); }); }}} {{{ DB::transaction(function () use ($request) { $validatedData = $request->validate([ 'imeaktivnost' => 'required|string|max:255', 'informacii' => 'nullable|string|max:255', 'kategorija' => 'required|string|max:255', 'iznos' => 'nullable|numeric', ]); TravelActivity::create($validatedData); }); }}} === Складирани функции и процедури Функциите и процедурите се дефинирани во базата и можат да се повикуваат од апликацијата. На тој начин се централизира логиката и се олеснува одржувањето. 1. Просечна оцена за локација (функција) Може да се користи кога прикажуваме детали за локација или за сортирање по оцена. {{{ CREATE OR REPLACE FUNCTION avg_rating_for_location(dest_id INT) RETURNS FLOAT AS $$ DECLARE result FLOAT; BEGIN SELECT AVG(ocenka) INTO result FROM recenzii WHERE iddest = dest_id; RETURN result; END; $$ LANGUAGE plpgsql; }}} 2. Додади препорака (процедура) {{{ CREATE OR REPLACE PROCEDURE dodadi_preporaka(p_korisnik INT, p_dest INT) LANGUAGE plpgsql AS $$ BEGIN INSERT INTO preporaki(idkorisnik, iddestinacija, datum) VALUES (p_korisnik, p_dest, NOW()); END; $$; }}}