== Напреден апликативен развој Имплементирани се сите случаи на употреба дефинирани во фаза 3. === Примери на индекси Индексите се користат за да се забрза извршувањето на често користени SELECT, JOIN и WHERE. Со оглед на тоа што во 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. Деактивирање дестинација со негативни рецензии {{{ 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); }); }}} 3. {{{ 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); }); }}} 4. {{{ 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; $$; }}} Овие складирани елементи се користат за централизирана логика и можат да се повикаат од Laravel или други процедури.