Krzysztof Biernacki

Krzysztof Biernacki programista PHP

Temat: [admin-generator] Jak okiełznać to "ustrojstwo"?

Witajcie.
Z Symfony 1.4 pracuję od niedawna. Wcześniej miałem styczność tylko z wersją 1.0, więc uprzejmie proszę o wyrozumiałość i pomoc. Ogólnie rzecz ujmując, mam problem z admin-generatorem i relacjami artykułów z kategoriami.
Może na początek pytanie: czy wszelkie akcje, moduły etc. wygenerowane przy użyciu admin-generatora są dostępne tylko i wyłącznie w katalogu 'cache'? Czy jest możliwość ich "usytuowania" na stałe w jednym miejscu - tak jak w przypadku CRUD w apps/backend/modules? Chciałbym nieco zmodyfikować wygląd i funkcjonalność, ale za każdym razem gdy cache jest czyszczony efekty mojej pracy znikają.

Poza tym, w bazie danych posiadam m.in. takie oto tabele i rekordy:

Tabela 'Kategoria'
| id | kategoria_id | nazwa |

Tabela 'hashująca' - 'Kategoria_has_Artykul'
| kategoria_id | artykul_id |

Tabela 'Artykul'
| id | tytul | tresc |

Na stronie właściwej mają się znajdować dwie główne kategorie: 'dla mieszkańca', 'dla studenta' (w przyszłości możliwość dodania nowej grupy tematycznej). Oprócz tego ma być dostępne menu składające się z kategorii i podkategorii (podkategorie mają się wyświetlać po najechaniu na kategorie). Nazwy kategorii i podkategorii są stałe (menu jest globalne i wygląda zawsze tak samo, bez względu na wybór grupy tematycznej - np.: 'dla studenta'), z tymże skojarzone z nimi artykuły są zależne od kategorii głównych: 'dla mieszkańca', 'dla studenta'.
Czyli przykładowo: Klikamy 'dla mieszkańca', następnie 'Wiadomości' (kategoria), 'Prasa' (podkategoria) i na podstronie wyświetlają się artykuły skojarzone z tymi działami. Kiedy wybierzemy 'dla studenta'->'Wiadomości'->'Prasa' mają się wyświetlić artykuły dla grupy tematycznej związanej ze studiami.

Stworzyłem formsy dla kategorii, artykułów - dane są zapisywane do bazy, jak i pomyślnie z niej odczytywane, ale... W jaki sposób zrobić powiązania artykułów z kategoriami? Chciałbym w formularzu dodawania nowego artykułu, bądź przy jego edycji, mieć drzewko z kategoriami do wyboru.

Mój plik 'generator.yml' wygląda następująco:
generator:
class: sfDoctrineGenerator
param:
model_class: Article
theme: admin
non_verbose_templates: true
with_show: false
singular: ~
plural: ~
route_prefix: article
with_doctrine_route: true
actions_base_class: sfActions

config:
actions: ~
fields:
title: { label: Tytuł, help: Tytuł artykułu }
thumb: { label: Miniaturka, help: Miniaturka }
created_at: { label: Data utworzenia }
updated_at: { label: Data aktualizacji }
is_active: { label: Aktywny }
list:
title: Zarządzanie artykułami
layout: stacked
display: [title, thumb, created_at, updated_at, is_active]
max_per_page: 30
filter:
display: [created_at]
form: ~
edit:
title: Edycja artykułu - "%%title%%"
new:
title: Dodaj artykuł

Natomiast plik 'ArticleForm.class.php' przedstawia się następująco:

class ArticleForm extends BaseArticleForm
{
protected static $artIsActive = array('true' => 'Tak', 'false' => 'Nie');

public function configure() {

$this->disableCSRFProtection();

$this->setWidgets(array(
'title' => new sfWidgetFormInput(array(), array('class' => 'artInput')),
'keywords' => new sfWidgetFormInput(array(), array('class' => 'artInput')),
'description' => new sfWidgetFormTextarea(array(), array('class' => 'no-editor')),
'text' => new sfWidgetFormTextarea(),
'image' => new sfWidgetFormInput(array(), array('class' => 'artInput')),
'created_at' => new sfWidgetFormDate(array('format' => '%year% - %month% - %day%')),
'updated_at' => new sfWidgetFormDate(array('format' => '%year% - %month% - %day%')),
'is_active' => new sfWidgetFormSelect(array('choices' => self::$artIsActive))
));

$this->widgetSchema->setLabels(array(
'title' => 'Tytuł',
'keywords' => 'Słowa kluczowe',
'description' => 'Skrócony opis (description)',
'text' => 'Treść',
'image' => 'Zdjęcie (charakterystyczne)',
'created_at' => 'Data utworzenia',
'updated_at' => 'Data modyfikacji',
'is_active' => 'Aktywny'
));

$this->setValidators(array(
'title' => new sfValidatorString(array('min_length' => 3, 'max_length' => 255), array(
'required' => 'Wpisz tytuł artykułu.',
'min_length' => 'Tytuł artykułu jest za krótki. Musi składać się z co najmniej %min_length% znaków.',
'max_length' => 'Tytuł artykułu jest za długi. Może składać się z co najwyżej %max_length% znaków.'
)),
'keywords' => new sfValidatorString(array('required' => false
)),
'description' => new sfValidatorString(array('min_length' => 3, 'max_length' => 255), array(
'required' => 'Wpisz skrócony opis artykułu.',
'min_length' => 'Skrócony opis artykułu jest za krótki. Musi składać się z co najmniej %min_length% znaków.',
'max_length' => 'Skrócony opis artykułu jest za długi. Może składać się z co najwyżej %max_length% znaków.'
)),
'text' => new sfValidatorString(array('min_length' => 3), array(
'required' => 'Wpisz treść artykułu.',
'min_length' => 'Treść artykułu jest za krótka. Musi składać się z co najmniej %min_length% znaków.'
)),
'image' => new sfValidatorString(array(
'required' => false
)),
'created_at' => new sfValidatorDate(array(
'required' => false
)),
'updated_at' => new sfValidatorDate(array(
'required' => false
)),
'is_active' => new sfValidatorChoice(array(
'choices' => array_keys(self::$artIsActive),
'required' => false
)),
));

}
}

Pomoże ktoś? Byłbym wdzięczny.

konto usunięte

Temat: [admin-generator] Jak okiełznać to "ustrojstwo"?

po pierwsze: po co Ci w tabeli kategoria id oraz kategoria_id? To nie jest to samo w tym przypadku?

Gdybyś dobrze wpisał w schema relacje wiele-do-wielu pomiędzy tabelami kategorii i artykułów, to automatycznie by Ci się wygenerował odpowiedni widget w bazowym formularzu. Popatrz jak w np pluginie sfGuardPlugin są zdefiniowane relacje pomiędzy tabelami sf_guard_user oraz sf_guard_permission za pomocą sf_guard_user_permission. Jeżeli nie będziesz potrafił z tego skorzystać i wyciągnąć wniosków, to może pomoże Ci ta strona http://www.symfonyreference.com/schema-yml. A jeżeli dalej będziesz miał problemy, to wklej tutaj zawartość schema.yml, tylko najlepiej w tagach [.code][./code] (bez kropek) - wtedy będzie to ładniej sformatowane:)

Odnośnie samych akcji generatora - one się specjalnie wpisują do cache, żeby domyślnie były wygenerowane odpowiednie, działające akcje. Jeżeli chcesz w jakiś sposób spersonalizować daną akcję, to po prostu skopiuj ją z pliku actions.class.php w cache do apps/backend/modules/twoj_modul/actions/actions.class.php i tam ją wyedytuj:)

Jeszcze jakieś pytania?:D
Krzysztof Biernacki

Krzysztof Biernacki programista PHP

Temat: [admin-generator] Jak okiełznać to "ustrojstwo"?

Michał Majewski:
po pierwsze: po co Ci w tabeli kategoria id oraz kategoria_id? To nie jest to samo w tym przypadku?

Nie, to nie jest to samo.
kategoria_id to potomek id kategorii (dany artykuł może należeć do kilku kategorii).

Plik schema.yml (relacje między artykułami i kategoriami) wygląda u mnie następująco:


Article:
connection: doctrine
tableName: article
columns:
id:
type: integer(4)
fixed: false
unsigned: false
primary: true
autoincrement: true
title:
type: string(255)
fixed: false
unsigned: false
primary: false
notnull: true
autoincrement: false
keywords:
type: string(255)
fixed: false
unsigned: false
primary: false
notnull: false
autoincrement: false
description:
type: string()
fixed: false
unsigned: false
primary: false
notnull: false
autoincrement: false
text:
type: string()
fixed: false
unsigned: false
primary: false
notnull: true
autoincrement: false
image:
type: string(100)
fixed: false
unsigned: false
primary: false
notnull: false
autoincrement: false
created_at:
type: timestamp(25)
fixed: false
unsigned: false
primary: false
notnull: true
autoincrement: false
updated_at:
type: timestamp(25)
fixed: false
unsigned: false
primary: false
notnull: true
autoincrement: false
is_active:
type: enum(5)
fixed: false
unsigned: false
values:
- 'true'
- 'false'
primary: false
default: 'false'
notnull: true
autoincrement: false
Category:
connection: doctrine
tableName: category
columns:
id:
type: integer(4)
fixed: false
unsigned: false
primary: true
autoincrement: true
category_id:
type: integer(4)
fixed: false
unsigned: false
primary: false
notnull: true
autoincrement: false
name:
type: string(150)
fixed: false
unsigned: false
primary: false
notnull: true
autoincrement: false
is_active:
type: enum(5)
fixed: false
unsigned: false
values:
- 'true'
- 'false'
primary: false
default: 'false'
notnull: true
autoincrement: false
relations:
Category:
local: id
foreign: category_id
type: many
CategoryHasArticle:
local: id
foreign: category_id
type: many
CategoryHasArticle:
connection: doctrine
tableName: category_has_article
columns:
category_id:
type: integer(4)
fixed: false
unsigned: false
primary: true
autoincrement: false
article_id:
type: integer(4)
fixed: false
unsigned: false
primary: true
autoincrement: false
relations:
Category:
local: category_id
foreign: id
type: one
Article:
local: article_id
foreign: id
type: one
Krzysztof Biernacki edytował(a) ten post dnia 26.08.11 o godzinie 19:47

konto usunięte

Temat: [admin-generator] Jak okiełznać to "ustrojstwo"?

Spróbuj tego. Może zadziała:)

Article:
actAs: [Timestampable]
columns:
id:
type: integer(4)
primary: true
autoincrement: true
title:
type: string(255)
notnull: true
keywords:
type: string(255)
description:
type: string()
text:
type: string()
image:
type: string(100)
is_active:
type: enum(5)
values:
- 'true'
- 'false'
default: 'false'
notnull: true
relations:
Categories:
class: Article
refClass: CategoryHasArticle
local: article_id
foreign: category_id
foreignAlias: Articles
Category:
columns:
id:
type: integer(4)
primary: true
autoincrement: true
category_id:
type: integer(4)
notnull: true
name:
type: string(150)
notnull: true
is_active:
type: enum(5)
values:
- 'true'
- 'false'
default: 'false'
notnull: true
relations:
Category:
local: id
foreign: category_id
type: many
Articles:
class: Category
local: category_id
foreign: article_id
refClass: CategoryHasArticle
foreignAlias: Articles
CategoryHasArticle:
tableName: category_has_article
columns:
category_id:
type: integer(4)
primary: true
autoincrement: false
article_id:
type: integer(4)
primary: true
relations:
Category:
local: category_id
onDelete: CASCADE
Article:
local: article_id
onDelete: CASCADE


przy okazji odchudziłem Twoją schemę - wielu rzeczy nie musisz podawać, ponieważ są one domyślnie ustawione. W moim poprzednim poście dałem Ci linka do strony, gdzie to wszystko jest dokładnie wyjaśnione:)
Krzysztof Biernacki

Krzysztof Biernacki programista PHP

Temat: [admin-generator] Jak okiełznać to "ustrojstwo"?

Plik schema.yml był automatycznie wygenerowany przez MySQL Workbench, stąd te dodatkowe znaczniki.
Dzięki za zainteresowanie tematem. Zabieram się do pracy.
Oprócz tego, zastrzegam sobie prawo do zadawania kolejnych pytań, w razie niejasności i problemów ;)
Krzysztof Biernacki

Krzysztof Biernacki programista PHP

Temat: [admin-generator] Jak okiełznać to "ustrojstwo"?

Mam kolejne pytania:
w jaki sposób skonfigurować plik generator.yml, bądź inne pliki znajdujące się w module, aby możliwe było sortowanie - 'ord_up', 'or_down'?
Ponadto w jaki sposób wykonać taką oto operację: klikam na 'edytuj' przy wybranym artykule, pojawia mi się formularz i w nim pole 'data edycji'. Natomiast przy opcji 'nowy' pole 'data utworzenia'?

Czy jest jakaś sensowna książka do Symfony w wersji 1.4 pokroju "Symfony 1.3 - web application development"? Te znajdujące się na oficjalnej stronie frameworka, nie wyczerpują tematu.Krzysztof Biernacki edytował(a) ten post dnia 03.09.11 o godzinie 10:26

konto usunięte

Temat: [admin-generator] Jak okiełznać to "ustrojstwo"?

Domyślnie sortowanie działa, chyba że zmieniasz list.display na inne pola, wtedy sortowanie nie wie jak się zachować i go po prostu nie ma:D zawsze można nadpisać templatkę, która wyświetla linki do sortowań - bodajże _list_th_tabular.php. Ale jak to dokładnie zrobić, to musisz najlepiej sam obczaić (nie jest to trudne, wystarczy pomyśleć) - bo nie potrafię tego wytłumaczyć przez internet.

Co do samego formularza - chodzi Ci o wyświetlenie innego pola w zależności czy formularz jest nowy, czy edytowany? Wystarczy zrobić w klasie formularza coś na zasadzie:

public function configure()
{
//...
if ($this->isNew())
{
$this->widgetSchema['created_at'] = new tutajNazwaKlasyWidgetu();
$this->validatorSchema['created_at'] = new tutajNazwaKlasyValidatora();
}
else
{
$this->widgetSchema['updated_at'] = new tutajNazwaKlasyWidgetu();
$this->validatorSchema['updated_at'] = new tutajNazwaKlasyValidatora();
}
//...
}


Ja nigdy nie korzystałem z innych książek do symfony jak te, które znajdują się na stronie frameworka (i to z tych również rzadko) i jakoś mi się udało okiełznać to "ustrojstwo":PMichał Majewski edytował(a) ten post dnia 03.09.11 o godzinie 14:43
Krzysztof Biernacki

Krzysztof Biernacki programista PHP

Temat: [admin-generator] Jak okiełznać to "ustrojstwo"?

Jakoś nie "leży" mi ten cały admin-generator.
Dzięki serdeczne za odzew i fragment kodu, który działa bez zarzutu. Oto mi właśnie chodziło.
Odnośnie "sortowania" - chodzi mi o taki "myk", który umożliwi ustalanie np. pozycji danej kategorii na liście, przy użyciu strzałek - w górę, w dół...

Przypomniało mi się jeszcze coś takiego: jak w klasie walidatora danego formularza dokonać ograniczenia wgrywania zdjęć o maksymalnej objętości załóżmy 3 MB, w formacie tylko i wyłącznie .jpg?

Na dzień dzisiejszy mam coś takiego:

$this->setValidators(array(
'image' => new sfValidatorFile(array(
'mime_types' => 'web_images',
'path' => sfConfig::get('sf_upload_dir').'/arts'), array(
'required' => 'Zdjęcie artykułu jest wymagane.'
))
));


-----------

edit: Z tym walidatorem do zdjęć już sobie poradziłem.
Dla zainteresowanych:

$this->setValidators(array(
'image' => new sfValidatorFile(
array('mime_types' => array('image/jpg', 'image/jpeg'),
'max_size' => 3000000,
'path' => sfConfig::get('sf_upload_dir').'/arts'),
array('max_size' => 'Maksymalna wielkość pliku wynosi 3 MB.',
'mime_types' => 'Obsługiwany format pliku graficznego - JPG/JPEG'
))
Krzysztof Biernacki edytował(a) ten post dnia 03.09.11 o godzinie 16:27

konto usunięte

Temat: [admin-generator] Jak okiełznać to "ustrojstwo"?

Krzysztof Biernacki:
Jakoś nie "leży" mi ten cały admin-generator.
Dzięki serdeczne za odzew i fragment kodu, który działa bez zarzutu. Oto mi właśnie chodziło.
Odnośnie "sortowania" - chodzi mi o taki "myk", który umożliwi ustalanie np. pozycji danej kategorii na liście, przy użyciu strzałek - w górę, w dół...

Admin-generator ma naprawdę duży potencjał, backend i częsć frontendu robi się błyskawicznie przy jego użyciu, wystarczy chwilę poświęcić na poznanie tego narzędzia. Ja też na początku się tego bałem, ale teraz...uwielbiam sobie dla relaksu "pyknąć" generator :D
Jeszcze jak obczaisz jak dokładnie jest to wszystko generowane, to nie będzie dla Ciebie problemem stworzyć własny theme, realizujący dane zadanie. Niech przykładem będziegenerator do galerii, który wyświetla na danej stronie np 4x4 zdjęcia. Oczywiście ilość zdjęć w pionie i poziomie definiujesz w yamlu:D Do tego sortowanie zdjęć, filtrowanie i paginacja...;) Takich przykładów użycia tego "ustrojstwa" jest naprawdę wiele i ograniczeniem jest tylko Twoja wyobraźnia:)

A to, o czym mówisz nie jest domyślnie wbudowane w generator, musiałbyś go rozwinąć o taką funkcjonalność.

A co do walidatorów - jeżeli nie wiesz jak coś w nim ustawić, to najpierw zajrzyj do wnętrza klasy walidatora, często w niej znajduje się dokładny opis wszystkich ustawień. A jeżeli brakuje Ci czegoś w danym walidatorze nic nie stoi na przeszkodzie zeby stworzyc wlasny walidator, w którym będziesz walidował co tylko chcesz:DMichał Majewski edytował(a) ten post dnia 03.09.11 o godzinie 22:35
Krzysztof Biernacki

Krzysztof Biernacki programista PHP

Temat: [admin-generator] Jak okiełznać to "ustrojstwo"?

A mógłbyś podać jakiś link do strony z wyżej wymienioną dodatkową funkcjonalnością? Rozumiem, że do klasy Category trzeba by dodać dwie metody w stylu: public function ordDown() oraz public function ordUp().
Mam jeszcze problem z czymś takim: w formularzu z artykułem załączam zdjęcie i przy użyciu pluginu sfThumbnailPlugin tworzę jego miniaturę. Sęk w tym, że załączone zdjęcie się zapisuje do konkretnego katalogu, do bazy danych również (tyle że w postaci hasha), a po miniaturce ani widu ani słychu. Generalnie chciałbym, aby zdjęcie załączane przez użytkownika, jak i miniatura owego zdjęcia, były przeskalowane i zapisane w postaci: image_$Idartykulu oraz thumb_$Idartykulu.

W klasie Article mam taki oto kod:

class Article extends BaseArticle
{
public function getArticleId() {
return $this->getId();
}

public function setImage($value) {

// w tym wypadku zmienna $value ma postać hasha: c407c811454b6edb68dc9f384842934752039666.jpg

$artPictMaxWidth = sfConfig::get('app_artMaxWidth'); // maksymalna długość miniatury zdjęcia
$uploadPath = sfConfig::get('sf_upload_dir').'/arts/';
$thumbPrefix = 'thumb_'; // prefiks dla miniatury zdjęcia

$oldImage = $this->getImage(); // zwraca wartość NULL

if (!empty($oldImage) && is_file($uploadPath.$thumbPrefix.$oldImage)) {
unlink($uploadPath.$thumbPrefix.$oldImage);
}

if (!empty($value) && is_file($uploadPath.$value)) {
$thumb = new sfThumbnail($artPictMaxWidth, '', true, false, 90);
$thumb->loadFile($uploadPath.$value);
$thumb->save($uploadPath.$thumbPrefix.$value);

$rescale = new sfThumbnail($artPictMaxWidth+300, '', true, false, 100);
$rescale->loadFile($uploadPath.$value);
$rescale->save($uploadPath.$value);
}

$imagePrefix = 'image_'; // prefiks dla zdjęcia dużego
$artId = parent::getArticleId();
$newV = $imagePrefix.$artId;

parent::setImage($newV);
parent::setThumb($thumbPrefix.$value);
}
}

konto usunięte

Temat: [admin-generator] Jak okiełznać to "ustrojstwo"?

ta dodatkowa funkcjonalnosc jest do napisania przez Ciebie:D a co do tego pluginu, to nigdy z niego nie korzystalem, zawsze miniaturki robilem recznie poprzez passthru i uzycie imagemagick

Następna dyskusja:

[Symfony2] Admin Generator




Wyślij zaproszenie do