{"id":31385,"date":"2019-10-31T21:40:58","date_gmt":"2019-10-31T18:40:58","guid":{"rendered":"https:\/\/prohoster.info\/blog\/biznes-logika-v-baze-dannyh-pri-pomoshhi-schemakeeper\/"},"modified":"2019-10-31T21:40:58","modified_gmt":"2019-10-31T18:40:58","slug":"biznes-logika-v-baze-dannyh-pri-pomoshhi-schemakeeper","status":"publish","type":"post","link":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/biznes-logika-v-baze-dannyh-pri-pomoshhi-schemakeeper","title":{"rendered":"Logika biznesowa w bazie danych za pomoc\u0105 SchemaKeeper","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Celem tego artyku\u0142u jest pokazanie narz\u0119dzi, kt\u00f3re mog\u0105 znacznie upro\u015bci\u0107 proces rozwijania baz danych w ramach projekt\u00f3w PHP, wykorzystuj\u0105cych systemy zarz\u0105dzania bazami danych PostgreSQL, na przyk\u0142adzie biblioteki <noindex><a rel=\"nofollow\" href=\"https:\/\/packagist.org\/packages\/schema-keeper\/schema-keeper\">schema-keeper<\/a><\/noindex> . Informacje zawarte w tym artykule b\u0119d\u0105 przede wszystkim przydatne dla deweloper\u00f3w, kt\u00f3rzy chc\u0105 maksymalnie wykorzysta\u0107 mo\u017cliwo\u015bci PostgreSQL, ale napotykaj\u0105 problemy z utrzymywaniem logiki biznesowej przeniesionej do bazy danych.<\/p>\n<p><\/p>\n<p>Artyku\u0142 nie b\u0119dzie opisywa\u0142 zalet ani wad przechowywania logiki biznesowej w bazie danych. Zak\u0142ada si\u0119, \u017ce wyb\u00f3r zosta\u0142 ju\u017c dokonany przez czytelnika. <\/p>\n<p><\/p>\n<p>Zostan\u0105 om\u00f3wione nast\u0119puj\u0105ce kwestie:<\/p>\n<p><\/p>\n<p>W jakiej formie przechowywa\u0107 zrzut struktury bazy danych w systemie kontroli wersji (dalej w tek\u015bcie \u2014 VCS)<\/p>\n<p><\/p>\n<ol>\n<li>Jak \u015bledzi\u0107 zmiany w strukturze bazy danych po zapisaniu zrzutu<\/li>\n<li>Jak przenosi\u0107 zmiany w strukturze bazy danych na inne \u015brodowiska bez konflikt\u00f3w i gigantycznych plik\u00f3w migracji<\/li>\n<li>Jak zorganizowa\u0107 proces r\u00f3wnoleg\u0142ej pracy nad projektem kilku deweloper\u00f3w<\/li>\n<li>Jak bezpiecznie wprowadza\u0107 wi\u0119ksz\u0105 ilo\u015b\u0107 zmian w strukturze bazy danych na \u015brodowisko produkcyjne<\/li>\n<li>SchemaKeeper<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><br \/>\n<blockquote><p><strong>jest zaprojektowany do pracy z procedurami sk\u0142adowanymi napisanymi w j\u0119zyku<\/strong> PL\/pgSQL <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/plpgsql.html\">. Testowanie z innymi j\u0119zykami nie by\u0142o przeprowadzane, dlatego u\u017cycie mo\u017ce by\u0107 mniej efektywne lub niemo\u017cliwe.<\/a><\/noindex>W jakiej formie przechowywa\u0107 zrzut struktury bazy danych w VCS\n<\/p><\/blockquote>\n<\/li>\n<\/ol>\n<p><\/p>\n<h2 id=\"v-kakom-vide-hranit-damp-struktury-bd-v-vcs\">oferuje funkcj\u0119<\/h2>\n<p><\/p>\n<p>Biblioteka <noindex><a rel=\"nofollow\" href=\"https:\/\/packagist.org\/packages\/schema-keeper\/schema-keeper\">schema-keeper<\/a><\/noindex> saveDump <code>, kt\u00f3ra zapisuje struktur\u0119 wszystkich obiekt\u00f3w z bazy danych w postaci osobnych plik\u00f3w tekstowych. Na wyj\u015bciu tworzony jest katalog zawieraj\u0105cy struktur\u0119 bazy danych, podzielon\u0105 na pogrupowane pliki, kt\u00f3re \u0142atwo doda\u0107 do VCS.<\/code>Rozwa\u017cmy konwersj\u0119 obiekt\u00f3w z bazy danych na pliki na kilku przyk\u0142adach:<\/p>\n<p><\/p>\n<p>Typ obiektu<\/p>\n<p><\/p>\n<p>\u015acie\u017cka wzgl\u0119dna do pliku<br \/>\nSchemat<br \/>\nNazwa<br \/>\nTabela<\/p>\n<p>accounts<br \/>\npublic<br \/>\n.\/public\/tables\/accounts.txt<br \/>\n<code>Procedura sk\u0142adowana<\/code><\/p>\n<p>auth(hash bigint)<br \/>\npublic<br \/>\n.\/public\/functions\/auth(int8).sql<br \/>\n<code>booking<\/code><\/p>\n<p>Reprezentacja<br \/>\ntariffs<br \/>\n.\/booking\/views\/tariffs.txt<br \/>\n<code>Zawarto\u015b\u0107 plik\u00f3w to tekstowa reprezentacja struktury konkretnego obiektu bazy danych. Na przyk\u0142ad, dla procedur sk\u0142adowanych zawarto\u015b\u0107 pliku b\u0119dzie pe\u0142n\u0105 definicj\u0105 procedury sk\u0142adowanej, zaczynaj\u0105c\u0105 si\u0119 od bloku<\/code><\/p>\n<p><\/p>\n<p>CREATE OR REPLACE FUNCTION <code>Jak wida\u0107 z powy\u017cszej tabeli, \u015bcie\u017cka do pliku zawiera informacje o typie, schemacie i nazwie obiektu. Takie podej\u015bcie u\u0142atwia nawigacj\u0119 po zrzucie oraz przegl\u0105d zmian w bazie danych.<\/code>.<\/p>\n<p><\/p>\n<p>.sql<\/p>\n<p><\/p>\n<blockquote><p>Rozszerzenie <code>.sql<\/code> dla plik\u00f3w z kodem \u017ar\u00f3d\u0142owym procedur przechowywanych, wybrano, aby IDE automatycznie dostarcza\u0142y narz\u0119dzia do interakcji z baz\u0105 danych przy otwieraniu pliku.<\/p><\/blockquote>\n<p><\/p>\n<h2 id=\"kak-otslezhivat-izmeneniya-v-strukture-bd-posle-sohraneniya-dampa\">Jak przenosi\u0107 zmiany w strukturze bazy danych na inne \u015brodowiska bez konflikt\u00f3w i gigantycznych plik\u00f3w migracji<\/h2>\n<p><\/p>\n<p>Zapisuj\u0105c zrzut bie\u017c\u0105cej struktury bazy danych w VCS, uzyskujemy mo\u017cliwo\u015b\u0107 sprawdzenia, czy wprowadzano zmiany w strukturze bazy po utworzeniu zrzutu. W bibliotece <noindex><a rel=\"nofollow\" href=\"https:\/\/packagist.org\/packages\/schema-keeper\/schema-keeper\">schema-keeper<\/a><\/noindex> w celu wykrycia zmian struktury bazy danych przewidziana jest funkcja <code>verifyDump<\/code>, kt\u00f3ra bez efekt\u00f3w ubocznych zwraca informacje o r\u00f3\u017cnicach. <\/p>\n<p><\/p>\n<p>Alternatywny spos\u00f3b sprawdzenia polega na ponownym wywo\u0142aniu funkcji <code>, kt\u00f3ra zapisuje struktur\u0119 wszystkich obiekt\u00f3w z bazy danych w postaci osobnych plik\u00f3w tekstowych. Na wyj\u015bciu tworzony jest katalog zawieraj\u0105cy struktur\u0119 bazy danych, podzielon\u0105 na pogrupowane pliki, kt\u00f3re \u0142atwo doda\u0107 do VCS.<\/code>, wskazuj\u0105c t\u0119 sam\u0105 katalog, i sprawdzeniu w VCS obecno\u015bci zmian. Poniewa\u017c wszystkie obiekty z bazy danych s\u0105 zapisane w osobnych plikach, VCS poka\u017ce tylko zmienione obiekty.<br \/>\nG\u0142\u00f3wn\u0105 wad\u0105 tego sposobu jest konieczno\u015b\u0107 nadpisania plik\u00f3w, aby zobaczy\u0107 zmiany.<\/p>\n<p><\/p>\n<h2 id=\"kak-perenosit-izmeneniya-v-strukture-bd-na-drugie-okruzheniya-bez-konfliktov-i-gigantskih-faylov-migraciy\">Jak zorganizowa\u0107 proces r\u00f3wnoleg\u0142ej pracy nad projektem kilku deweloper\u00f3w<\/h2>\n<p><\/p>\n<p>Dzi\u0119ki funkcji <code>deployDump<\/code> kod \u017ar\u00f3d\u0142owy procedur przechowywanych mo\u017cna edytowa\u0107 w taki sam spos\u00f3b jak zwyk\u0142y kod aplikacji. Mo\u017cna dodawa\u0107\/usuwa\u0107 nowe linie w kodzie procedur przechowywanych i natychmiast przesy\u0142a\u0107 zmiany do systemu kontroli wersji lub tworzy\u0107\/usuwa\u0107 procedury przechowywane, tworz\u0105c\/usuwaj\u0105c odpowiednie pliki w katalogu ze zrzutem. <\/p>\n<p><\/p>\n<p>Na przyk\u0142ad, aby utworzy\u0107 now\u0105 procedur\u0119 przechowywan\u0105 w schemacie <code>public<\/code> , wystarczy stworzy\u0107 nowy plik z rozszerzeniem <code>.sql<\/code> w katalogu <code>public\/functions<\/code>, umie\u015bci\u0107 w nim kod \u017ar\u00f3d\u0142owy procedury przechowywanej, w tym blok <code>Jak wida\u0107 z powy\u017cszej tabeli, \u015bcie\u017cka do pliku zawiera informacje o typie, schemacie i nazwie obiektu. Takie podej\u015bcie u\u0142atwia nawigacj\u0119 po zrzucie oraz przegl\u0105d zmian w bazie danych.<\/code>, a nast\u0119pnie wywo\u0142a\u0107 funkcj\u0119 <code>deployDump<\/code>. Podobnie odbywa si\u0119 modyfikacja i usuwanie procedur przechowywanych. W ten spos\u00f3b kod trafia jednocze\u015bnie do VCS i do bazy danych. <\/p>\n<p><\/p>\n<p>Je\u015bli w kodzie \u017ar\u00f3d\u0142owym jakiejkolwiek procedury przechowywanej wyst\u0105pi b\u0142\u0105d, lub niezgodno\u015b\u0107 mi\u0119dzy nazw\u0105 pliku a procedur\u0105 przechowywan\u0105, to <code>deployDump<\/code> nie zostanie wykonana, wy\u015bwietlaj\u0105c komunikat o b\u0142\u0119dzie. Niesp\u00f3jno\u015b\u0107 procedur przechowywanych mi\u0119dzy zrzutem a bie\u017c\u0105c\u0105 baz\u0105 danych jest niemo\u017cliwa przy u\u017cyciu <code>deployDump<\/code>.<\/p>\n<p><\/p>\n<blockquote><p>Podczas tworzenia nowej procedury przechowywanej nie ma potrzeby r\u0119cznego wprowadzania poprawnej nazwy pliku. Wystarczy, \u017ce plik ma rozszerzenie <code>.sql<\/code>. Po wywo\u0142aniu <code>deployDump<\/code> tekst b\u0142\u0119du b\u0119dzie zawiera\u0142 poprawn\u0105 nazw\u0119, kt\u00f3r\u0105 mo\u017cna wykorzysta\u0107 do zmiany nazwy pliku.<\/p><\/blockquote>\n<p><code>deployDump<\/code> pozwala zmienia\u0107 parametry funkcji lub typ zwracany bez dodatkowych dzia\u0142a\u0144, podczas gdy w klasycznym podej\u015bciu nale\u017ca\u0142oby<br \/>\nnajpierw wykona\u0107 <code>DROP FUNCTION<\/code>, a dopiero potem <code>Jak wida\u0107 z powy\u017cszej tabeli, \u015bcie\u017cka do pliku zawiera informacje o typie, schemacie i nazwie obiektu. Takie podej\u015bcie u\u0142atwia nawigacj\u0119 po zrzucie oraz przegl\u0105d zmian w bazie danych.<\/code>.<\/p>\n<p><\/p>\n<p>Niestety, istniej\u0105 sytuacje, w kt\u00f3rych <code>deployDump<\/code> nie mo\u017cna automatycznie zastosowa\u0107 zmian. Na przyk\u0142ad, je\u015bli usuni\u0119ta zostanie funkcja wyzwalaj\u0105ca, kt\u00f3ra jest u\u017cywana przez przynajmniej jeden wyzwalacz. Takie sytuacje wymagaj\u0105 r\u0119cznego rozwi\u0105zania za pomoc\u0105 plik\u00f3w migracji.<\/p>\n<p><\/p>\n<p>Je\u015bli za przenoszenie zmian w procedurach sk\u0142adowych odpowiada <noindex><a rel=\"nofollow\" href=\"https:\/\/packagist.org\/packages\/schema-keeper\/schema-keeper\">schema-keeper<\/a><\/noindex>, to do przeniesienia pozosta\u0142ych zmian w strukturze konieczne jest wykorzystanie plik\u00f3w migracji. Na przyk\u0142ad, dobr\u0105 bibliotek\u0105 do pracy z migracjami jest <noindex><a rel=\"nofollow\" href=\"https:\/\/packagist.org\/packages\/doctrine\/migrations\">doctrine\/migrations<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Migracje powinny by\u0107 stosowane przed uruchomieniem <code>deployDump<\/code>. Umo\u017cliwia to wprowadzenie wszystkich zmian w strukturze i rozwi\u0105zanie problematycznych sytuacji, aby zmiany w procedurach sk\u0142adowych p\u00f3\u017aniej przesz\u0142y bez problem\u00f3w.<\/p>\n<p><\/p>\n<p>Szczeg\u00f3\u0142owe informacje na temat pracy z migracjami b\u0119d\u0105 opisane w kolejnych rozdzia\u0142ach.<\/p>\n<p><\/p>\n<h2 id=\"kak-naladit-process-parallelnoy-raboty-nad-proektom-neskolkih-razrabotchikov\">Jak bezpiecznie wprowadza\u0107 wi\u0119ksz\u0105 ilo\u015b\u0107 zmian w strukturze bazy danych na \u015brodowisko produkcyjne<\/h2>\n<p><\/p>\n<p>Nale\u017cy utworzy\u0107 skrypt pe\u0142nej inicjalizacji bazy danych, kt\u00f3ry b\u0119dzie uruchamiany przez programist\u0119 na jego lokalnej maszynie, dostosowuj\u0105c struktur\u0119 lokalnej bazy danych do zapisanej w VCS zrzutu. Naj\u0142atwiej jest podzieli\u0107 inicjalizacj\u0119 lokalnej bazy danych na 3 kroki:<\/p>\n<p><\/p>\n<ol>\n<li>Import pliku z podstawow\u0105 struktur\u0105, kt\u00f3ry b\u0119dzie nazywa\u0142 si\u0119 na przyk\u0142ad <code>base.sql<\/code> <\/li>\n<li>Zastosowanie migracji<\/li>\n<li>Wywo\u0142anie <code>deployDump<\/code><\/li>\n<\/ol>\n<p><\/p>\n<blockquote><p><code>base.sql<\/code> \u2014 to punkt wyj\u015bcia, na kt\u00f3rym stosowane s\u0105 migracje i wykonywane <code>deployDump<\/code>, czyli <code>base.sql + migracje + deployDump = aktualna struktura bazy danych<\/code>. Taki plik mo\u017cna wygenerowa\u0107 za pomoc\u0105 narz\u0119dzia <code>pg_dump<\/code>. U\u017cywane jest <code>base.sql<\/code> wy\u0142\u0105cznie przy inicjalizacji bazy danych od zera.<\/p><\/blockquote>\n<p>Nazwijmy skrypt pe\u0142nej inicjalizacji bazy danych <code>refresh.sh<\/code>. Proces roboczy mo\u017ce wygl\u0105da\u0107 nast\u0119puj\u0105co:<\/p>\n<p><\/p>\n<ol>\n<li>Programista uruchamia w swoim \u015brodowisku <code>refresh.sh<\/code> i otrzymuje aktualn\u0105 struktur\u0119 bazy danych<\/li>\n<li>Programista zaczyna prac\u0119 nad zadaniem, modyfikuj\u0105c lokaln\u0105 baz\u0119 danych zgodnie z potrzebami nowej funkcjonalno\u015bci (<code>ALTER TABLE ... ADD COLUMN<\/code> itd.)<\/li>\n<li>Po zako\u0144czeniu zadania programista wywo\u0142uje funkcj\u0119 <code>, kt\u00f3ra zapisuje struktur\u0119 wszystkich obiekt\u00f3w z bazy danych w postaci osobnych plik\u00f3w tekstowych. Na wyj\u015bciu tworzony jest katalog zawieraj\u0105cy struktur\u0119 bazy danych, podzielon\u0105 na pogrupowane pliki, kt\u00f3re \u0142atwo doda\u0107 do VCS.<\/code>, aby zarejestrowa\u0107 w VCS zmiany dokonane w bazie danych<\/li>\n<li>Programista ponownie uruchamia <code>refresh.sh<\/code>, nast\u0119pnie <code>verifyDump<\/code>, kt\u00f3ry teraz pokazuje list\u0119 zmian do uwzgl\u0119dnienia w migracji<\/li>\n<li>Programista przenosi wszystkie zmiany strukturalne do pliku migracji, uruchamia jeszcze raz <code>refresh.sh<\/code> i <code>verifyDump<\/code>, a je\u015bli migracja zostanie poprawnie utworzona, <code>verifyDump<\/code> poka\u017ce brak r\u00f3\u017cnic mi\u0119dzy lokaln\u0105 baz\u0105 danych a zapisanym zrzutem.<\/li>\n<\/ol>\n<p><\/p>\n<p>Opisana wy\u017cej procedura jest zgodna z zasadami gitflow. Ka\u017cda ga\u0142\u0105\u017a w VCS b\u0119dzie zawiera\u0107 swoj\u0105 wersj\u0119 dumpa, a podczas scalania ga\u0142\u0119zi nast\u0105pi scalanie dump\u00f3w. W wi\u0119kszo\u015bci przypadk\u00f3w po scaleniu nie trzeba podejmowa\u0107 \u017cadnych dodatkowych dzia\u0142a\u0144, ale je\u017celi w r\u00f3\u017cnych ga\u0142\u0119ziach wprowadzono zmiany, na przyk\u0142ad w tej samej tabeli, mo\u017ce wyst\u0105pi\u0107 konflikt.<\/p>\n<p><\/p>\n<p>Rozwa\u017cmy konfliktow\u0105 sytuacj\u0119 na przyk\u0142adzie: jest ga\u0142\u0105\u017a <em>develop<\/em>, od kt\u00f3rej odga\u0142\u0119zi\u0142y si\u0119 dwie ga\u0142\u0119zie: <em>feature1<\/em> i <em>feature2<\/em>, kt\u00f3re nie maj\u0105 konflikt\u00f3w z <em>develop<\/em>, ale maj\u0105 konflikty mi\u0119dzy sob\u0105. Zadaniem jest wykonanie scalania obu ga\u0142\u0119zi w <em>develop<\/em>. W takim przypadku zaleca si\u0119 najpierw scali\u0107 jedn\u0105 z ga\u0142\u0119zi w <em>develop<\/em>, a nast\u0119pnie scali\u0107 <em>develop<\/em> do pozosta\u0142ej ga\u0142\u0119zi, rozwi\u0105zuj\u0105c przy tym konflikty w pozosta\u0142ej ga\u0142\u0119zi, a nast\u0119pnie wykona\u0107 scalanie ostatniej ga\u0142\u0119zi w <em>develop<\/em>. Na etapie rozwi\u0105zywania konflikt\u00f3w mo\u017ce by\u0107 konieczne poprawienie pliku migracji w ostatniej ga\u0142\u0119zi, aby odpowiada\u0142 on ostatecznemu dumpowi, kt\u00f3ry uwzgl\u0119dnia wyniki scalenia.<\/p>\n<p><\/p>\n<h2 id=\"kak-bezopasno-deploit-bolshee-kolichestvo-izmeneniy-v-strukture-bd-na-production-okruzhenie\">SchemaKeeper<\/h2>\n<p><\/p>\n<p>Dzi\u0119ki obecno\u015bci w VCS dumpa aktualnej struktury bazy danych istnieje mo\u017cliwo\u015b\u0107 sprawdzenia bazy produkcyjnej pod k\u0105tem dok\u0142adnego dopasowania do wymaganej struktury. Gwarantuje to, \u017ce wszystkie zmiany, kt\u00f3re zaplanowali deweloperzy, zosta\u0142y pomy\u015blnie przeniesione na baz\u0119 produkcyjn\u0105.<\/p>\n<p><\/p>\n<p>Poniewa\u017c <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/ddl.html\">DDL<\/a><\/noindex> w PostgreSQL jest <noindex><a rel=\"nofollow\" href=\"https:\/\/wiki.postgresql.org\/wiki\/Transactional_DDL_in_PostgreSQL:_A_Competitive_Analysis\">transakcyjny<\/a><\/noindex>, zaleca si\u0119 stosowanie nast\u0119puj\u0105cej kolejno\u015bci wdro\u017cenia, aby w przypadku nieprzewidzianego b\u0142\u0119du, \"bezbole\u015bnie\" wykona\u0107 <code>ROLLBACK<\/code>:<\/p>\n<p><\/p>\n<ol>\n<li>Rozpocznij transakcj\u0119<\/li>\n<li>W transakcji wykonaj wszystkie migracje<\/li>\n<li>W tej samej transakcji wykonaj <code>deployDump<\/code><\/li>\n<li>Nie ko\u0144cz\u0105c transakcji, wykonaj <code>verifyDump<\/code>. Je\u015bli nie ma b\u0142\u0119d\u00f3w, wykonaj <code>COMMIT<\/code>. Je\u015bli s\u0105 b\u0142\u0119dy, wykonaj <code>ROLLBACK<\/code><\/li>\n<\/ol>\n<p><\/p>\n<p>Te kroki s\u0105 wystarczaj\u0105co \u0142atwe do w\u0142\u0105czenia w istniej\u0105ce podej\u015bcia do wdra\u017cania aplikacji, w tym zero-downtime.<\/p>\n<p><\/p>\n<h1 id=\"zaklyuchenie\">Podsumowanie<\/h1>\n<p><\/p>\n<p>Dzi\u0119ki opisanym powy\u017cej metodom mo\u017cna wycisn\u0105\u0107 maksimum wydajno\u015bci z projekt\u00f3w \"PHP + PostgreSQL\", po\u015bwi\u0119caj\u0105c przy tym stosunkowo niewielk\u0105 ilo\u015b\u0107 wygody programowania w por\u00f3wnaniu z wdro\u017ceniem ca\u0142ej logiki biznesowej w podstawowym kodzie aplikacji. Co wi\u0119cej, przetwarzanie danych w <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/plpgsql.html\">. Testowanie z innymi j\u0119zykami nie by\u0142o przeprowadzane, dlatego u\u017cycie mo\u017ce by\u0107 mniej efektywne lub niemo\u017cliwe.<\/a><\/noindex> cz\u0119sto wygl\u0105da bardziej przejrzy\u015bcie i wymaga mniejszej ilo\u015bci kodu ni\u017c ta sama funkcjonalno\u015b\u0107 napisana w PHP.<\/p>\n<p>\u0179r\u00f3d\u0142o: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/447746\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0426\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u2014 \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 schema-keeper \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043e\u0431\u043b\u0435\u0433\u0447\u0438\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0431\u0430\u0437 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 PHP-\u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0438\u0445 \u0421\u0423\u0411\u0414 PostgreSQL. \u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0438\u0437 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438, \u0432 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u043d\u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e \u043c\u0430\u043a\u0441\u0438\u043c\u0443\u043c\u0443 \u0445\u043e\u0442\u044f\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 PostgreSQL, \u043d\u043e \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0441 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430\u043c\u0438 \u0441\u043e\u043f\u0440\u043e\u0432\u043e\u0436\u0434\u0435\u043d\u0438\u044f \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438, \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u043d\u043e\u0439 \u0432 \u0411\u0414. \u0421\u0442\u0430\u0442\u044c\u044f \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-31385","post","type-post","status-publish","format-standard","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0426\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u2014 \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 schema-keeper \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/biznes-logika-v-baze-dannyh-pri-pomoshhi-schemakeeper\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"pl_PL\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0411\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430 \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 SchemaKeeper | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0426\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u2014 \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 schema-keeper \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/biznes-logika-v-baze-dannyh-pri-pomoshhi-schemakeeper\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T18:40:58+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T18:40:58+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Logika biznesowa w bazie danych za pomoc\u0105 SchemaKeeper | ProHoster","description":"Celem tego artyku\u0142u jest na przyk\u0142adzie biblioteki schema-keeper pokazanie narz\u0119dzi, kt\u00f3re mog\u0105 znacz\u0105co.","canonical_url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/biznes-logika-v-baze-dannyh-pri-pomoshhi-schemakeeper","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pl_PL","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0411\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430 \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 SchemaKeeper | ProHoster","og:description":"\u0426\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u2014 \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 schema-keeper \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e.","og:url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/biznes-logika-v-baze-dannyh-pri-pomoshhi-schemakeeper","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T18:40:58+00:00","article:modified_time":"2019-10-31T18:40:58+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"31385","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-21 05:54:24","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 03:17:26","updated":"2026-01-21 05:54:24","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/31385","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/comments?post=31385"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/31385\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media?parent=31385"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/categories?post=31385"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/tags?post=31385"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}