Drugie starcie – zebrana wiedza [KO3.2]

Postanowiłem zebrać dotychczasową wiedzę w jedną całość i stworzyć aplikację na aktualnej Kohanie – KO3.2, która zawierała będzie:
- szablon strony
- moduł Auth: rejestracja, logowanie i prawa
- połączenia do bazy – ORM
- walidację formularzy
- tłumaczenia językowe
- moduł Gmaps
- coś o routingu

Stworzona strona to prosta aplikacja. Zawiera jednak pewne mechanizmy, których zrozumienie może być kluczowe w nauce Kohany. Na stworzonej stronie możemy się zarejestrować i zalogować. Zalogowany użytkownik może: dodać/edytować artykuł, zaznaczyć na mapie swoją lokalizację, zobaczyć lokalizację innych. Niezalogowany ma ograniczony dostęp, z kolei administrator może edytować artykuły innych użytkowników.

Jako szablon strony wykorzystałem: templatemo 203 corporate (923)podgląd

Przyjrzyjmy się plikom:

/application/bootstrap.php »

Kohana::init(array(
	'base_url'   => '/',
        'index_file' => FALSE,
));
Kohana::modules(array(
	 'auth'       => MODPATH.'auth',       // Basic authentication
	 'database'   => MODPATH.'database',   // Database access
	 'orm'        => MODPATH.'orm',        // Object Relationship Mapping
         'gmaps'        => MODPATH.'gmaps',        // Google Maps
	));
Route::set('auth', '<action>', array('action' => 'log(in|out)|register'))
    ->defaults(array(
        'controller' => 'auth',
        ));
Route::set('static', '<action>', array('action' => 'about|contact'))
    ->defaults(array(
        'controller' => 'static',
        ));
Route::set('default', '(<controller>(/<action>(/<id>(/<id2>))))')
	->defaults(array(
		'controller' => 'default',
		'action'     => 'index',
	));

W bootstrapie wyłączono wyświetlanie w linku index.php, włączono moduły auth, database, orm, gmaps (niestandardowy), a także dodano routing statyczny. Routing zawsze wybiera pierwszą dopasowaną regułę, stąd należy umieszczać je przed defaultowym routingiem. Jak wiadomo wg defaultowego routingu link wygląda tak: example.com/kontroler/akcja/param1. Standardowa akcja kontrolera to index (wywołuje się po wpisaniu example.com/kontroler). Dla statycznych stron tj. rejestracja, logowanie, o firmie, czy formularz kontaktowy nie musimy tworzyć osobnych kontrolerów i w akcjach index wyświetlać te strony. Możemy stworzyć statyczny routing, w którym nie trzeba będzie podawać nazwy kontrolera, a strony będą się wczytywać po wpisaniu example.com/akcja. Zostało to zrobione dla kontrolerów auth i static. Pierwszy posiada akcje login, logout i register, drugi about i contact. Kontroler wczytywany jako domyślny to kontroler Default.


Kontroler Default

/application/classes/controller/default.php »

<?php defined('SYSPATH') or die('No direct script access.');

class Controller_Default extends Controller_Template {

    public $template = 'default';

    public function  __construct(Request $request, Response $response) {
        parent::__construct($request, $response);

        if(isset($_COOKIE['lang'])){
            setcookie("lang", $_COOKIE['lang'],31536000 + time(),'/');
            I18n::lang($_COOKIE['lang']);
        }
    }

    public function before() {
      parent::before();

        if ($this->auto_render)
        {
            // Initialize empty values
            $this->template->title = '';
            $this->template->description = '';
            $this->template->content = '';

            $this->template->styles = array();
            $this->template->scripts = array();
            $this->template->top_tab = '';
        }
    }

    public function after() {
        if ($this->auto_render)
        {
                $styles = array(
                        'media/css/style.css' => 'screen',
                );

                $scripts = array(
                        'media/js/jquery-1.6.2.min.js',
                );

                $this->template->styles = array_merge( $this->template->styles, $styles );
                $this->template->scripts = array_merge( $this->template->scripts, $scripts );
                $this->template->langs=Kohana::$config->load('lang');
        }
        parent::after();
    }

    public function action_index() {
        $this->template->content=View::factory('home');
        $this->template->title = __('Home');
        $this->template->top_tab='home';
    }
}
?>

Kontroler Default rozszerza kontroler Template. A więc aby wczytać widok o nazwie default.php (szablon strony) musimy zadeklarować:

public $template = 'default';

W konstruktorze kontrolera Default sprawdzamy czy w cookie jest wybrany język strony (wysłany tam wcześniej), jeśli jest to aktualizujemy czas życia ciastka i przełączamy na ten język.

if(isset($_COOKIE['lang'])){
            setcookie("lang", $_COOKIE['lang'],31536000 + time(),'/');
            I18n::lang($_COOKIE['lang']);
        }

W before deklarujemy pewne zmienne (style, skrypty), w after przypisujemy im wartości. W akcji index wysyłamy do zmiennej content widok home, do szablonu wysyłamy tytuł strony i zmienną top_tab, która odpowiada za zaznaczanie aktualnej zakładki.

Szablon strony:

/application/views/default.php »

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title><?php echo $title?> | Kohana</title>
<meta name="keywords" content="corporate website, free css templates, CSS, HTML" />
<meta name="description" content="<?php echo $description?>" />
<?php foreach ($styles as $file => $type) echo HTML::style($file, array('media' => $type)), "\n" ?>
<?php foreach ($scripts as $file) echo HTML::script($file), "\n" ?>
</head>
<body>
<div id="templatemo_header_wrapper">
	<div id="templatemo_header">

        <div id="site_title">
            <a href="/" target="_parent">
                <img src="/media/img/templatemo_logo.png" alt="Logo" />
                <span>FREE CSS TEMPLATES</span>
            </a>
      </div>


        <div id="templatemo_menu">

            <ul>
                <?php echo'<li>'.HTML::anchor('/', __('Home'), ($top_tab=="home" ? array('class'=>'current') : NULL)).'</li>';?>
                <?php echo'<li>'.HTML::anchor('article', __('Articles'), ($top_tab=="article" ? array('class'=>'current') : NULL)).'</li>';?>
                <?php echo'<li>'.HTML::anchor('about', __('About us'), ($top_tab=="about" ? array('class'=>'current') : NULL)).'</li>';?>
                <?php echo'<li>'.HTML::anchor('contact', __('Contact'), ($top_tab=="contact" ? array('class'=>'current') : NULL)).'</li>';?>
                <?php echo'<li>'.(Auth::instance()->get_user()==NULL ?  HTML::anchor('register', __('Register'), ($top_tab=="register" ? array('class'=>'current') : NULL)) : HTML::anchor('user', __('My account'), ($top_tab=="user" ? array('class'=>'current') : NULL))).'</li>';?>
                <?php echo'<li>'.(Auth::instance()->get_user()==NULL ?  HTML::anchor('login', __('Login'), ($top_tab=="login" ? array('class'=>'current') : NULL)) : HTML::anchor('logout', __('Logout'))).'</li>';?>
            </ul>

        </div> <!-- end of templatemo_menu -->

    </div>
</div>

<div id="templatemo_feature_wrapper">
	<div id="templatemo_feature">

		<div id="feature_left">

        	<h1>Welcome to CSS Template</h1>
            <p>Duis vitae velit sed dui malesuada dignissim. Donec mollis aliquet ligula. Maecenas adipiscing elementum ipsum. Pellentesque vitae magna. Sed nec est. Suspendisse a nibh tristique justo rhoncus volutpat. Suspendisse vitae neque eget ante tristique vestibulum.</p>
            <div class="button"><a href="#">Read more</a></div>

        </div>

        <div id="feature_right">

        	<div id="feature_image"><span></span>
            	<img src="/media/img/templatemo_image_01.png" alt="image" />
            </div>

        </div>

	</div> <!-- end of feature -->
</div> <!-- end of feature warpper -->

<div id="templatemo_content_wrapper">

    <div id="templatemo_content">

        <?php echo $content //Kohana::find_file('views', $content) ? include Kohana::find_file('views', $content) : print($content);?>

    </div> <!-- end of content -->

    <div id="templatemo_sidebar">

        <h2>Latest <span>News</span></h2>
        <div class="newsbox">
        	<div class="news_image"><span></span><img src="/media/img/templatemo_image_04.png" alt="image" /></div>
            <p>Aenean cursus. Maecenas aliqu am, ligula id egestas suscipit, nisi sapien dignissim. </p>
            <div class="cleaner"></div><div class="readmore"><a href="#">read more</a></div>
        </div>
        <div class="newsbox">
        	<div class="news_image"><span></span><img src="/media/img/templatemo_image_05.png" alt="image" /></div>
            <p>estibulum ullamcorper eros vel lectus sagittis aliquet aliquam ante pretium. </p>
            <div class="cleaner"></div><div class="readmore"><a href="#">read more</a></div>
        </div>
        <div class="newsbox">
        	<div class="news_image"><span></span><img src="/media/img/templatemo_image_06.png" alt="image" /></div>
            <p>Praesent pretium purus ut lorem luctus ornare interdum diam eleifend. </p>
            <div class="cleaner"></div><div class="readmore"><a href="#">read more</a></div>
        </div>

        <div id="testimonial_newsletter">
            <div id="testimonial">
                <h2>Testimonial</h2>
                <p><img class="open" src="/media/img/open.png" alt="opening tag" />urabitur adipiscing tellus eu arcu tincidunt condimentum. Aliquam ipsum felis, pulvinar at lacinia at, commodo ut nisi. Nullam egestas fermentum nis.<img class="close" src="/media/img/close.png" alt="closing tag" /></p>
                <cite class="float_r"><a href="#">Vestibulum</a></cite>            </div>

            <div id="newsletter">
            	<h2>Newsletter</h2>
                <form action="#" method="get">

                	<input type="text" id="newsletter_email" />
                    <input type="submit" name="submit" value="Subscribe" id="newsletter_subscribe" />
                </form>
            </div>
    	</div>

    </div> <!-- end of slider -->

    <div class="cleaner_h30"></div>

    <div id="latest_projects">

	<div id="latest_project_text">
            <h2>Latest Projects</h2>
            <p>	Sedut perspiciatis unde omnis iste natus error sit volup tatem accus antium dolorem que lauda ntiumAt vero eos et accusamus et iusto odio.</p>
        </div>

        <div class="project_image_box margin_r15">
            <div class="project_image"><span></span>
            	<img src="/media/img/templatemo_image_07.png" alt="image 1" />
            </div>
            <h5><a href="#">sed ut perspiciatis.</a></h5>
	  </div>

	<div class="project_image_box margin_r15">
            <div class="project_image"><span></span>
            	<img src="/media/img/templatemo_image_08.png" alt="image 2" />
            </div>
        	<h5><a href="#">emque laudantium</a></h5>
	  </div>

	<div class="project_image_box margin_r15">
            <div class="project_image"><span></span>
            	<img src="/media/img/templatemo_image_09.png" alt="image 3" />
            </div>
        <h5><a href="#">eaque ipsa quae</a></h5>
	  </div>

      <div class="project_image_box">
	<div class="project_image"><span></span>
            	<img src="/media/img/templatemo_image_10.png" alt="image 4" />
            </div>
          <h5><a href="#">similique sunt.</a></h5>
	  </div>

    </div>

	<div  class="cleaner"></div>
</div> <!-- end of content wrapper -->

<div id="templatemo_footer_wrapper">
	<div id="templatemo_footer">
    	<div id="footer_title">Site Info.</div>

        <div class="footer_box">
        	<div class="cleaner_h40"></div>
        	<p>Copyright © 2011 <a href="/">Kohana Template</a><br />
        	<a href="http://www.iwebsitetemplate.com" target="_parent">Website Templates</a> by <a href="http://www.templatemo.com" target="_parent">Free CSS Templates</a></p>
                <p>
                <?php
                foreach($langs as $key => $lang){
                    echo HTML::anchor('lang/'.$key, $lang);
                    echo $key!='pl-pl' ? ' | ' : '';
                }?>
            </p>
        </div>

        <div class="footer_box">
        	<h2>Our Sponsor</h2>
            <div class="blog_image"><span></span><img src="/media/img/templatemo_image_11.png" alt="web template" /></div>
            <p class="blog_section"><span>Monday, November 2010</span>
            Suscipit, nisi sapien dignissimnibh, ac vestibulum lorem urna in neque.</p>
      </div>

        <div class="footer_box">
       		<h2>Business partners</h2>
            <ul>
            	<li><a href="#">Cum sociis natoque penatibus</a></li>
                <li><a href="#">Vestibulum auctor odio eget</a></li>
                <li><a href="#">Praesent sollicitudin mollis</a></li>
                <li><a href="#">Quisque blandit eros et lorem</a></li>
            </ul>
        </div>

        <div class="cleaner"></div>
    </div>
</div>

</body>
</html>

Zawiera kod html z szablonem. W nagłówku wczytujemy w pętli style i skrypty, ustawiamy tytuł i opis. W topie w templatemo_menu ustawiamy wykrywanie aktualnej strony i w zależności czy użytkownik jest zalogowany czy nie wyświetlamy różne linki. W divie templatemo_content wyświetlamy zmienną content która może zawierać podwidok, lub string do wyświetlenia. W stopce w footer_box wyświetlane są linki do przełączania języków.

Widok home

/application/views/home.php »

<h2>Welcome to <span>Corporate Template</span></h2>

<p>This <a href="http://www.templatemo.com" target="_parent">free web template</a> is provided by <a href="http://www.templatemo.com" target="_parent">templatemo.com</a> website. You may use this template in your websites. Credit goes to <a href="http://www.photovaco.com" target="_blank">Free Photos</a> for photos. Validate <a href="http://validator.w3.org/check?uri=referer">XHTML</a> &amp; <a href="http://jigsaw.w3.org/css-validator/check/referer">CSS</a>.</p>
<p>Fusce ac orci sit amet velit ultrices condimentum. Integer imperdiet odio ac eros. Ut id massa. Nullam nunc. Vivamus sagittis varius lorem.Maecenas aliquam, ligula id egestas suscipit, nisi sapien dignissim nibh, ac vestibulum lorem urna in neque.</p>
<div class="readmore"><a href="#">read more</a></div>

<div class="cleaner_h40"></div>

<h2>Our <span>Services</span></h2>
<p>	Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illovitae dicta sunt explicabo.</p>

<div class="col_w265 float_l">
    <h3>Web Design</h3>
    <div class="two_col_image">
        <a href="#"><img src="/media/img/templatemo_image_02.png" alt="image" width="249" height="105" /></a>            </div>
  <p>Aenean cursus aesf aecenas aliquam, ligula idegers estas suscipit, nisisapien dignissim nibh, acvestibulum lore mur nain neque. </p>
    <div class="readmore"><a href="#">read more</a></div>
</div>

<div class="col_w265 float_r">
    <h3>Photography</h3>
    <div class="two_col_image">
        <a href="#"><img src="/media/img/templatemo_image_03.png" alt="image" width="249" height="105" /></a> </div>
  <p>Phasellus convallis facilisis risus vitae fringilla. Aliquam imperdiet, est at scelerisque tincidunt, orci ipsum mattis est.</p>
    <div class="readmore"><a href="#">read more</a></div>
</div>

Ładowany jest na stronę główną, zawiera tylko html z szablonu.


Lang

Konfig lang

/application/config/lang.php »

<?php defined('SYSPATH') or die('No direct script access.');

return array(
    'en-us' => 'English',
    'pl-pl' => 'Polski',
);

Zawiera listę dostępnych języków

Kontroler Lang

/application/classes/controller/lang.php »

<?php defined('SYSPATH') or die('No direct script access.');

class Controller_Lang extends Controller {

    public function before() {
        parent::before();
        $lang = $this->request->action();
        I18n::lang($lang);
        setcookie("lang", $lang,31536000 + time(),'/');
        $this->request->redirect();
    }

    public function action_index(){

    }
}
?>

W before tego kontrolera przypisywany jest do zmiennej lang język z akcji (np. example.com/lang/pl-pl). Następnie zmieniany jest język, tworzone jest ciastko i przekierowujemy na stronę główną.

Plik tłumaczący na język polski

/application/i18n/pl.php »

<?php defined('SYSPATH') or die('No direct script access.');

return array(
    //validation
    ':field must contain only letters' => ':field musi zawierać tylko litery',
    ':field must contain only numbers, letters and dashes' => ':field musi zawierać tylko cyfry, litery i kreski',
    ':field must contain only letters and numbers' => ':field musi zawierać tylko litery i cyfry',
    ':field must be a color' => ':field musi być kolorem',
    ':field must be a credit card number' => ':field musi być numerem karty kredytowej',
    ':field must be a date' => ':field musi być datą',
    ':field must be a decimal with :param2 places' => ':field musi być ułamkiem z :param2 miejscami po przecinku',
    ':field must be a digit' => ':field musi być cyfrą',
    ':field must be a email address' => ':field musi być adresem email',
    ':field must contain a valid email domain' => ':field musi zawierać prawidłową domenę emaili',
    ':field must equal :param2' => ':field musi równać się :param2',
    ':field must be exactly :param2 characters long' => ':field musi mieć dokładnie :param2 znaków długości',
    ':field must be one of the available options' => ':field musi być jedną z dostępnych opcji',
    ':field must be an ip address' => ':field musi być adresem ip',
    ':field must be the same as :param2' => ':field musi być takie same jak :param2',
    ':field must be at least :param2 characters long' => ':field musi być dłuższe niż :param2 znaków',
    ':field must not exceed :param2 characters long' => ':field musi być krótsze niż :param2 znaków',
    ':field must not be empty' => ':field nie może być puste',
    ':field must be numeric' => ':field musi być numeryczne',
    ':field must be a phone number' => ':field musi być numerem telefonu',
    ':field must be within the range of :param2 to :param3' => ':field musi być pomiędzy :param2 a :param3',
    ':field does not match the required format' => ':field nie spełnia wymaganego formatu',
    ':field must be a url' => ':field musi być adresem url',

    //form
    'Password'=>'Hasło',
    'Confirm password'=>'Powtórz hasło',
    'register.email.unique'=>':field musi być unikalny',
    'register.username.unique'=>':field musi być unikalny',

    'Welcome'=>'Witaj',
    'Home'=>'Główna',
    'Articles'=>'Artykuły',
    'Article'=>'Artykuł',
    'Contact'=>'Kontakt',
    'About us'=>'O nas',
    'Register'=>'Zarejestruj',
    'My account'=>'Moje konto',
    'User'=>'Użytkownik',
    'Location'=>'Lokalizacja',
    'Search'=>'Szukaj',
    'Place to find'=>'Miejsce do znalezienia',
    'Actual location'=>'Aktualna lokalizacja',
    'Marker'=>'Znacznik',
    'Edit'=>'Edytuj',
    'Show'=>'Pokaż',
    'We are the champions'=>'Jesteśmy mistrzami',
    'Send'=>'Wyślij',
    'Do not have an account?'=>'Nie masz konta?',
    'Incorrect login or password'=>'Nieprawidłowy login bądź hasło',
    'Fill the fields'=>'Uzupełnij pola',
    'Login'=>'Zaloguj',
    'Logout'=>'Wyloguj',
    'Password'=>'Hasło',
    'Access error'=>'Brak dostępu',
    'Add an article'=>'Dodaj artykuł',
    'Edit the article'=>'Edytuj artykuł',
    'Title'=>'Tytuł',
    'Content'=>'Zawartość',
    'State'=>'Status',
    'Save'=>'Zapisz',
    'Active'=>'Aktywny',
    'Unactive'=>'Nieaktywny',
    'Author'=>'Autor',
    'Date'=>'Data',
    'Message was sent'=>'Wiadomość została wysłana',
    ''=>'',
    ''=>'',
    ''=>'',
    ''=>'',
    );
?>

Zawiera on odpowiedniki słów, wyrazów, zdań z en na pl


Auth

Konfig auth

/application/config/auth.php »

<?php defined('SYSPATH') or die('No direct access allowed.');

return array(

	'driver'       => 'ORM',
	'hash_method'  => 'sha256',
	'hash_key'     => hash_hmac('sha256', 'Wiadomosc do zahashowania', 'Tajny klucz'),
	'lifetime'     => 1209600,
	'session_type' => Session::$default,
	'session_key'  => 'auth_user',

	// Username/password combinations for the Auth File driver
	'users' => array(
		// 'admin' => 'b3154acf3a344170077d11bdb5fff31532f679a1919e716a02',
	),

);

Ustawiamy dane konfiguracyjne modułu Auth tj. sterownik, metodę hashowania, klucz hashowania, czas życia sesji, typ sesji, klucz sesji.

Kontroler Auth

/application/classes/controller/auth.php »

<?php defined('SYSPATH') or die('No direct script access.');

class Controller_Auth extends Controller_Default {

    public function action_login() {
        $this->template->content=View::factory('login')
                ->bind('error',$error)
                ->bind('title',$this->template->title);
        $this->template->title=__('Login');
        $this->template->top_tab='login';
        $this->template->error=NULL;
        if(isset($_POST['submit'])){
            $auth = Auth::instance()->login($_POST['username'], $_POST['password']);
            if(!$auth){
                $error='<p class="red">'.__('Incorrect login or password').'</p>';
            }else{
                $this->request->redirect('user');
            }
        }
    }
    
    public function action_logout() {
        Auth::instance()->logout();
        $this->request->redirect('/');
    }
    
    public function action_register() {
        $this->template->content=View::factory('register')
            ->bind('errors',$errors)
            ->bind('data',$data)
            ->bind('title',$this->template->title);
        $this->template->title=__('Register');
        $this->template->top_tab='register';

        if(isset ($_POST['submit'])){
            $rules=  Model_User::factory('user')->rules();
            $post = Validation::factory($_POST)
                 ->rules('email', $rules['email'])
                 ->rules('username', $rules['username'])
                 ->rules('password', $rules['password'])
                 ->rule('password_confirm', 'matches', array(':validation', 'password', 'password_confirm'))
                 ->labels(array('email'=>'E-mail','username'=>'Nick','password'=>'Password','password_confirm'=>'Confirm password'));

            if($post->check()){
                $client = ORM::factory('user');
                $client->email = $_POST['email'];
                $client->username = $_POST['username'];
                $client->password = $_POST['password'];
                $client->save();

                $role = ORM::factory('role','1'); //login role
                $client->add('roles',$role);
                $client->save();
                unset($_POST);
                $this->request->redirect('login');
            }else{
                $data=$_POST;
                $errors=$post->errors('register');
            }
        }
    }
}
?>

Dzięki odpowiedniemu routingowi akcję login wywołujemy url’em: example.com/login (pozostałe akcje analogicznie). W akcji login przesyłamy do zmiennej content widok login, a do tego widoku 2 zmienne (error i title) przez referencję (bind). Dzięki temu możemy wysłać do widoku zmienną, nie mając jeszcze zmiennej.

$this->template->content=View::factory('login')
                ->bind('error',$error)
                ->bind('title',$this->template->title);

Następnie ustawiamy tytuł strony (zostanie wysłany do szablonu i do widoku login), a także zaznaczamy aktywną zakładkę w topie. Sprawdzamy czy wysłano formularz. Jeśli wysłano to próbujemy zalogować się tymi danymi. Jeśli logowanie się nie powiedzie przypisujemy do zmiennej error treść błędu (dzięki referencji przypisanie to oznacza wysłanie do widoku treści błędu). Jeśli logowanie się powiedzie przekierowujemy na stronę użytkownika.

$this->request->redirect('user');

Widok login

/application/views/login.php »

<h2><?php echo $title?></h2>
<?php echo  Form::open('/login', array('class'=>'form'))?>
<fieldset>
<legend><?php echo __('Fill the fields')?></legend>
    <?php echo $error?>
    <?php echo Form::label('username', __('Nick').': ').Form::input('username')?><br />

    <?php echo Form::label('password', __('Password').': ').Form::password('password')?><br />
</fieldset>
<?php echo Form::submit('submit', __('Login')).Form::close();?><br />
<p><?php echo __('Do not have an account?').' '.HTML::anchor('register',__('Register'));?></p>

W nagłówku wyświetlany jest tytuł. Później wyświetlany jest formularz. Jeśli jest błąd zostaje wyświetlony na początku. Formularz zawiera input text i password. Przed nimi są etykiety – label. Jest przycisk submit do logowania, a także link do rejestracji.

W akcji logout wylogowujemy i przekierowujemy na stronę główną.

W akcji register wysyłamy do zmiennej content widok register i 3 zmienne przez referencje (bind): errors, data i title (dzięki temu zostanie jakby do widoku wysłany wskaźnik na zmienną, jeśli wystąpi zostanie wysłana). W errors będą komunikaty validacji, w data wartości wpisywane do inputów (początkowo puste, po wysłaniu formularza, wysłane wartości zmienną _post). Sprawdzamy czy formularz został wysłany, jeśli tak, to pobieramy z modelu user (moduł Auth) reguły walidacji i przypisujemy do odpowiednich pól. Reguły te można podejrzeć (w NetBeans’i Navigate->Go to Declaration), są tam między innymi reguły że email nie może być pusty, musi być prawidłowym adresem email i nie można podać drugi raz tego samego jak już jest w bazie, username też musi być unikalny. Przypisane reguły są w tablicy, stąd aby odwołać się do reguł danego pola, wpisujemy jego nazwę w nawiasach kwadratowych.

$rules=  Model_User::factory('user')->rules();
            $post = Validation::factory($_POST)
                 ->rules('email', $rules['email'])
                 ->rules('username', $rules['username'])
                 ->rules('password', $rules['password'])
                 ->rule('password_confirm', 'matches', array(':validation', 'password', 'password_confirm'))

Gdybyśmy mieli więcej pól przy rejestracji to własne reguły można dodać przez ->rule albo ->rules.

->rule('email', 'not_empty')
->rule('email', 'email')
->rule('name', 'max_length', array(':value', 20))
->rules('surname', array(array('not_empty'),array('max_length', array(':value', 30))))

Dostępne reguły możemy zobaczyć (jeśli mamy włączony moduł guide) wpisując adres example.com/guide/api/Valid.

Następnie zmieniamy nazwy inputów w komunikatach validacji na nazwy przyjazna ludziom, ponieważ błędy walidacji pobierają nazwę pola, więc komunikat ‚Nik nie może być puste’ wygląda lepiej nić ‚username nie może być puste’. Pola te są również tłumaczone na inne języki

->labels(array('email'=>'E-mail','username'=>'Nick','password'=>'Password','password_confirm'=>'Confirm password'));

Sprawdzamy czy walidacja przeszła

if($post->check()){

Jeśli tak, zapisujemy do bazy użytkownika

$client = ORM::factory('user');
$client->email = $_POST['email'];
$client->username = $_POST['username'];
$client->password = $_POST['password'];
$client->save();

I dodajemy mu rolę login (aby mógł się logować)

$role = ORM::factory('role','1'); //login role
$client->add('roles',$role);
$client->save();

Jeśli validacja nie przeszła (nie spełniliśmy którejś z reguł), do zmiennej errors przypisujemy (co jest równoznaczne z wysyłaniem) błędy validacji, a do inputów formularza (dzięki zmiennej data) przesyłamy wysłane wcześniej dane (przy wysłaniu formularza)

                $data=$_POST;
                $errors=$post->errors('register');

Widok register

/application/views/register.php »

<h2><?php echo $title?></h2>
<?php echo  Form::open('/register', array('class'=>'form'))?>
<fieldset>
<legend><?php echo __('Fill the fields')?></legend>

    <?php echo Form::label('email', __('E-mail').': ').Form::input('email',Arr::get($data, 'email'))?> <span class="red"><?php echo Arr::get($errors, 'email');?></span><br />
    <?php echo Form::label('username', __('Nick').': ').Form::input('username', Arr::get($data, 'username'))?> <span class="red"><?php echo Arr::get($errors, 'username');?></span><br />

    <?php echo Form::label('password', __('Password').': ').Form::password('password', Arr::get($data, 'password'))?> <span class="red"><?php echo Arr::get($errors, 'password');?></span><br />
    <?php echo Form::label('password_confirm', __('Confirm password').': ').Form::password('password_confirm', Arr::get($data, 'password_confirm'))?> <span class="red"><?php echo Arr::get($errors, 'password_confirm');?></span><br />
</fieldset>
<?php echo Form::submit('submit', __('Register')).Form::close();?><br />

Zawiera formularz do rejestracji. Nie musimy sprawdzać czy tablica errors z wartością email występuje (nawet jak jej nie prześlemy), dzięki

<?php echo Arr::get($errors, 'email');?>

A w momencie gdy wystąpi, zostanie po prostu wyświetlona. To samo tyczy się zmiennej data.


Kontroler Static

/application/classes/controller/static.php »

<?php defined('SYSPATH') or die('No direct script access.');

class Controller_Static extends Controller_Default {

    public function action_about() {
        $this->template->content='<h2>'.__('About us').'</h2><p>'.__('We are the champions').'</p>';
        $this->template->title=__('About us');
        $this->template->top_tab='about';
    }

    public function action_contact() {
        $this->template->content=View::factory('contact')
            ->bind('msg',$msg)
            ->bind('errors',$errors)
            ->bind('data',$data)
            ->bind('title',$this->template->title);
        $this->template->title=__('Contact');
        $this->template->top_tab='contact';
        if(isset($_POST['submit'])){
            $post=Validation::factory($_POST)
                ->rules('email', array(array('not_empty'),array('email')))
                ->rules('username', array(array('not_empty'),array('alpha_dash'),array('min_length', array(':value', 3)),array('max_length', array(':value', 20))))
                ->rule('content', 'not_empty')
                ->labels(array('email'=>'E-mail','username'=>'Nick','content'=>'Content'));
            if($post->check()){
                //send email
                $msg='<p class="green">'.__('Message was sent').'</p>';
                unset($_POST);
            }else{
                $data=$_POST;
                $errors=$post->errors('contact');
            }
        }
    }
}?>

Kontroler ten posiada 2 akcje (about i contact). Z racji iż jest odpowiedni routing do niego, aby wywołać akcję about wpisujemy w url: example.com/about (i odpowiednio contact). Akcja about nie przesyła do zmiennej content widoku, tylko string zawierający kod html. W akcji contact wysyłamy widok contact i deklarujemy zmienne (msg, errors, data i title). Sprawdzamy czy formularz został wysłany. Jeśli tak przypisujemy do zmiennej post validację i tworzymy odpowiednie reguły

$post=Validation::factory($_POST)
                ->rules('email', array(array('not_empty'),array('email')))
                ->rules('username', array(array('not_empty'),array('alpha_dash'),array('min_length', array(':value', 3)),array('max_length', array(':value', 20))))
                ->rule('content', 'not_empty')

Input o nazwie email nie może być pusty i musi być adresem email. Input zawierający nazwę użytkownika nie może być pusty, może zawierać tylko znaki alfanumeryczne, minimalna długość to 3, maksymalna to 20. Textarea o nazwie content nie może być pusta. Zostają także ustawione przyjazne ludziom nazwy w błędach validacji

->labels(array('email'=>'E-mail','username'=>'Nick','content'=>'Content'));

Jeśli walidacja się powiedzie, zostanie wysłany email (trzeba dodać funkcję), zostanie wyświetlony komunikat o sukcesie i zostanie usunięta zmienna _post. Jeśli walidacja się nie powiedzie, to zostaną przesłane treści błędów i dane _post z powrotem do formularza.

Widok contact

/application/views/contact.php »

<h2><?php echo $title?></h2>
<?php echo  Form::open('/contact', array('class'=>'form'))?>
<fieldset>
<legend><?php echo __('Fill the fields')?></legend>
    <?php echo $msg?>
    <?php echo Form::label('email', __('E-mail').': ').Form::input('email', Arr::get($data, 'email'))?> <span class="red"><?php echo  Arr::get($errors, 'email')?></span><br />
    <?php echo Form::label('username', __('Nick').': ').Form::input('username', Arr::get($data, 'username'))?> <span class="red"><?php echo Arr::get($errors, 'username')?></span><br />

    <?php echo Form::label('contetn', __('Content').': ',array('class'=>'fll')).Form::textarea('content', Arr::get($data, 'content'), array('cols'=>'40'))?> <span class="red"><?php echo  Arr::get($errors, 'content')?></span><br />
</fieldset>
<?php echo Form::submit('submit', __('Send')).Form::close();?><br />

Zawiera formularz kontaktowy.


Kontroler User

/application/classes/controller/user.php »

<?php defined('SYSPATH') or die('No direct script access.');

class Controller_User extends Controller_Default {

    public function action_index() {
        if (Auth::instance()->logged_in()) {
            $this->template->content=View::factory('user')
                ->bind('user',$user)
                ->bind('title',$this->template->title);
            $this->template->title = __('My account');
            $this->template->top_tab='user';
            $user=ORM::factory('user', Auth::instance()->get_user());
        }else{
            Request::initial()->redirect('login');
        }
    }
    public function action_show() {
        $id=$this->request->param('id');
        $this->template->content=View::factory('user_show')
                ->bind('user',$user)
                ->bind('title',$this->template->title);
        $user=ORM::factory('user',$id);
        $this->template->title = __('User').' '.$user->username;
        $this->template->top_tab='user';
        $lat=$user->lat ? $user->lat : 0;
        $lng=$user->lng ? $user->lng : 0;
        $this->template->content->staticmap=Gmaps::instance()->staticmap(array('color:green','label:A', $lat, $lng),'500x400',14,'roadmap');
    }
    public function action_map() {
        if(Auth::instance()->logged_in()) {
            $this->template->content=View::factory('user_map')
                ->bind('error',$error)
                ->bind('title',$this->template->title);
            $this->template->title = __('Location');
            $this->template->top_tab='user';

            $gmaps=Gmaps::instance();
            if(isset($_POST['search_submit'])){
                $search=$gmaps->geo_request($_POST['search']);

                if($search) {
                  $lat=$search->Point->coordinates[1];
                  $lng=$search->Point->coordinates[0];
                }else {
                  $lat='0';
                  $lng='0';
                  $error='<span style="color:red">No place find</span>';}
            }else{
                $user=ORM::factory('user', Auth::instance()->get_user());
                $lat=$user->lat ? $user->lat : 0;
                $lng=$user->lng ? $user->lng : 0;
            }

            $info='<strong>'.__('Actual location').'</strong>';
            $this->template->content->jscode=$gmaps->jsmap($lat, $lng, 14, 'ROADMAP',TRUE,TRUE,__('Marker'),$info,TRUE);

            if(isset($_POST['submit'])){
                $user=ORM::factory('user', Auth::instance()->get_user());
                $user->lat=$_POST['lat'] !='' ? $_POST['lat'] : NULL;
                $user->lng=$_POST['lng'] !='' ? $_POST['lng'] : NULL;
                $user->save();
                unset ($_POST);
                Request::initial()->redirect('user');
            }
        }else{
            Request::initial()->redirect('login');
        }
    }
}?>

Akcja index służy do wyświetlenia panelu użytkownika. Na początku sprawdzane jest, czy użytkownik jest zalogowany (jeśli nie następuje przekierowanie do logowania). Jeśli jest zalogowany zostaje przesłany widok user i obiekt user zawierający dane zalogowanego użytkownika.

Widok user

/application/views/user.php »

<h2><?php echo $title?></h2>
<p><?php echo __('Welcome').' '.$user->username?></p>
<p><?php echo HTML::anchor('user/map',__('Location'))?></p>

Zawiera odnośniki do opcji, które użytkownik może wykonać (w tej aplikacji tylko wyznaczyć lokalizację).

Akcja show służy do wyświetlenia danych o użytkowniku, którego id jest przesłany jako parametr: example.com/user/show/id. Zostaje wczytany widok user_show. Sprawdzane jest czy użytkownik zaznaczył swoją lokalizację, jeśli nie, marker zostaje ustawiony na współrzędne (0,0), jeśli współrzędne znajdują się w bazie, to zostaje przesłany obrazek z mapą z zaznaczonym punktem.

$lat=$user->lat ? $user->lat : 0;
$lng=$user->lng ? $user->lng : 0;
$this->template->content->staticmap=Gmaps::instance()->staticmap(array('color:green','label:A', $lat, $lng),'500x400',14,'roadmap');

Widok user_show

/application/views/user_show.php »

<h2><?php echo $title?></h2>
<div class="form">
    <label><?php echo __('Nick')?>: </label><span><?php echo $user->username?></span><br />
    <label><?php echo __('E-mail')?>: </label><span><?php echo $user->email?></span><br />
    <label><?php echo __('Articles')?>: </label><span><?php echo HTML::anchor('article/user/'.$user->id, __('Show'))?></span><br />
    <?php if(Auth::instance()->logged_in()){?>
    <label><?php echo __('Location')?>: </label><br /><img src="<?php echo $staticmap?>" alt="" />
    <?php }?>
</div>

Zawiera dane użytkownika, jeśli użytkownik jest zalogowany to dodatkowo wyświetlany jest obrazek z mapą na którym jest zaznaczona lokalizacja danego użytkownika.

Akcja map służy do wyszukiwania i zaznaczania markera na mapie i zapisywania do bazy współrzędnych. Poprzez zmienną _post search_submit i submit, sprawdza czy zostały wysłane formularze. Pierwszy służy do wyszukiwania, drugi do zapisywania w bazie współrzędnych. Aby użyć modułu Gmaps trzeba utworzyć instancję

$gmaps=Gmaps::instance();

Aby wyszukać dane o lokalizacji ze zmiennej z formularza wywołujemy

$search=$gmaps->geo_request($_POST['search']);

Jeśli nic nie znaleziono, ustawiane są współrzędne (0,0) i wysyłany jest komunikat, że nic nie znaleziono. Jeśli funkcja goe_request coś znalazła, to zostają pobrane i przypisane do zmiennych lat i lng współrzędne wyszukanej miejscowości

$lat=$search->Point->coordinates[1];
$lng=$search->Point->coordinates[0];

Jeśli nie szukaliśmy (nie ma zmiennej _post search_submit) zostają pobrane współrzędne zalogowanego użytkownika z bazy i zostają przypisane do zmiennych. Jeśli w bazie nie ma współrzędnych, to przypisujemy współrzędne (0,0)

$user=ORM::factory('user', Auth::instance()->get_user());
$lat=$user->lat ? $user->lat : 0;
$lng=$user->lng ? $user->lng : 0;

Do widoku user_map zostaje wysłana zmienna zawierająca kod javascript mapy

$info='<strong>'.__('Actual location').'</strong>';
$this->template->content->jscode=$gmaps->jsmap($lat, $lng, 14, 'ROADMAP',TRUE,TRUE,__('Marker'),$info,TRUE);

Gdy wyślemy drugi formularz (zmienna _post submit) zostaną zapisane współrzędne użytkownika w bazie

$user=ORM::factory('user', Auth::instance()->get_user());
                $user->lat=$_POST['lat'] !='' ? $_POST['lat'] : NULL;
                $user->lng=$_POST['lng'] !='' ? $_POST['lng'] : NULL;
                $user->save();

Zostanie usunięta zmienna _post i nastąpi przekierowanie na stronę z panelem użytkownika

unset ($_POST);
Request::initial()->redirect('user');

Widok user_map

/application/views/user_map.php »

<h2><?php echo $title?></h2>
<div>
<?php echo $jscode;?>
<?php echo  Form::open(Request::current()).
Form::label('search', __('Place to find').': ').
Form::input('search',NULL,array('size'=>'50')).
Form::submit('search_submit', __('Search')).
Form::close(); echo $error?>

<div id="map-canvas" style="width:550px;height:450px"></div>

<?php echo  Form::open(Request::current()).
Form::label('lat', 'Lat: ').
Form::input('lat',NULL,array('id'=>'lat')).
Form::label('lat', ' Lng: ').
Form::input('lng',NULL,array('id'=>'lng')).
Form::submit('submit', __('Save')).
Form::close();?>
</div>

Zawiera 2 formularze (do wyszukiwania miejscowości i zapisywania współrzędnych) a także div, do którego wczytana jest mapa.


Article

Model Article

/application/classes/model/article.php »

<?php defined('SYSPATH') OR die('No direct access allowed.');

class Model_Article extends ORM {

    protected $_belongs_to = array(
        'user' => array(),
    );
    public function save_data($data,$edit=NULL){
        $edit=isset($edit) ? $edit : FALSE;
        $valid=  Validation::factory($data)
            ->rules('title', array(array('not_empty'),array('min_length', array(':value', 4))))
            ->rules('content', array(array('not_empty'),array('min_length', array(':value', 100))))
            ->rule('state', 'digit')
            ->labels(array('title'=>'Title','content'=>'Content','state'=>'State'));
        if($valid->check()){
            $this->title=$data['title'];
            if(!$edit) $this->user_id=Auth::instance()->get_user();
            $this->content=$data['content'];
            $this->state=$data['state'];
            $this->date=date('Y-m-d H:i:s');
            $this->save();
            return false;
        }else{
            return $valid->errors('article');
        }
    }
}?>

Model ten ma relację belongs_to do modelu user, oznacza to, że konkretny artykuł jest powiązany tylko z jednym użytkownikiem. Model ten posiada jedną akcję – save_data, przyjmuje ona 2 parametry: pierwszy to dane do zapisania (wcześniej validacji), drugi oznacza edycję (przy edycji nie aktualizujemy użytkownika, bo edycji może dokonywać admin). Najpierw następuje validacja pól (tytuł nie może być pusty i krótszy niż 4 znaki, treść artykułu nie może być pusta i krótsza niż 100 znaków, status musi być liczbą).

$valid=  Validation::factory($data)
            ->rules('title', array(array('not_empty'),array('min_length', array(':value', 4))))
            ->rules('content', array(array('not_empty'),array('min_length', array(':value', 100))))
            ->rule('state', 'digit')

Zostają także ustawione przyjazne ludziom nazwy pól w komunikatach validacji.

->labels(array('title'=>'Title','content'=>'Content','state'=>'State'));

Jeśli validacja się powiedzie zostają zapisane dane do bazy. Funkcja save_data zwraca także błędy, stąd po poprawnej walidacji zwraca false, a jeśli validacja się nie powiedzie zostają zwrócone błędy

}else{
            return $valid->errors('article');
        }

Kontroler Article

/application/classes/controller/article.php »

<?php defined('SYSPATH') or die('No direct script access.');

class Controller_Article extends Controller_Default {

    public function action_index() {
        $this->template->content=View::factory('article')
            ->bind('articles',$articles)
            ->bind('title',$this->template->title);
        $this->template->title=__('Articles');
        $this->template->top_tab='article';
        $articles=ORM::factory('article')->where('state','=',1)->find_all();
    }

    public function action_add() {
        if (Auth::instance()->logged_in()) {
            $this->template->content=View::factory('article_save')
                ->bind('errors',$errors)
                ->bind('data',$data)
                ->bind('title',$this->template->title);
            $this->template->title=__('Add an article');
            $this->template->top_tab='article';
            if(isset($_POST['submit'])){
                $article=ORM::factory('article');
                $save_errors=$article->save_data($_POST);
                if($save_errors){
                   $data=$_POST;
                   $errors=$save_errors;
                }else{
                    $this->request->redirect('article');
                }
            }
        }else{
            Request::initial()->redirect('login');
        }
    }

    public function action_edit() {
        if (Auth::instance()->logged_in()) {
            $id=$this->request->param('id');
            $article=ORM::factory('article',$id);
            if($article->user_id==Auth::instance()->get_user() || Auth::instance()->logged_in('admin')){
                $this->template->content=View::factory('article_save')
                    ->bind('errors',$errors)
                    ->bind('data',$data)
                    ->bind('title',$this->template->title);
                $this->template->title=__('Edit the article');
                $this->template->top_tab='article';
                if(isset($_POST['submit'])){
                    $save_errors=$article->save_data($_POST,1);
                    if($save_errors){
                       $data=$_POST;
                       $errors=$save_errors;
                    }else{
                        $this->request->redirect('article');
                    }
                }else{
                    $data=$article->as_array();
                }
            }else{
                $this->template->title = __('Access error');
                $this->template->content='<h2 class="red">'.__('Access error').'</h2>';
            }
        }else{
            Request::initial()->redirect('login');
        }
    }

    public function action_show() {
        $id=$this->request->param('id');
        $article=ORM::factory('article',$id);
        if($article->state){
            $this->template->content=View::factory('article_show')
                ->bind('article',$article)
                ->bind('title',$this->template->title);
            $this->template->title=$article->title;
            $this->template->top_tab='article';
        }else{
            $this->template->title = __('Access error');
            $this->template->content='<h2 class="red">'.__('Access error').'</h2>';
        }
    }
    
    public function action_user() {
        $id=$this->request->param('id');
        $this->template->content=View::factory('article')
            ->bind('articles',$articles)
            ->bind('title',$this->template->title);
        $articles=ORM::factory('article')->where('user_id','=',$id)->and_where('state', '=', 1)->find_all();
        $this->template->title=__('Articles');
        $this->template->top_tab='article';
    }
}?>

Akcja index służy do wyświetlania wszystkich aktywnych artykułów. Zostaje wczytany widok article, zadeklarowane zmienne articles i title przez referencję (jak we wcześniejszych akcjach) zostaje ustawiony tytuł (w szablonie i w widoku article), zostaje zaznaczona zakładka articles w topie, a także zostają wyszukane artykuły

$articles=ORM::factory('article')->where('state','=',1)->find_all();

Widok article

/application/views/article.php »

<h2><?php echo $title?></h2>
<p><?php  echo Auth::instance()->logged_in() ? HTML::anchor('article/add', __('Add an article')) : ''?></p>
<?php
foreach ($articles as $article){?>
<h3><?php echo HTML::anchor('article/show/'.$article->id.'/'.URL::title($article->title,'-',TRUE),$article->title);?></h3>
    <p><?php echo HTML::anchor('user/show/'.$article->user_id,$article->user->username).' ('.$article->date.')'?><br /><?php echo mb_substr($article->content, 0, 100, 'UTF-8')?>...</p>
<?php }?>

Widok ten zawiera listę wszystkich artykułów lub artykułów użytkownika. Jeśli użytkownik jest zalogowany wyświetlony zostaje link do dodawania artykułu. W pętli dla wszystkich artykułów zostaje wyświetlone: link do artykułu (jako drugi parametr w linku – nic nie znaczący – zostaje przesłany tytuł tzw. przyjazny link, przydatne przy pozycjonowaniu), nazwa użytkownika, data, i 100 znaków z treści.

Akcja add na początku sprawdza czy jesteśmy zalogowanie, jeśli nie zostajemy przekierowani na stronę logowania, jeśli jesteśmy zalogowanie wczytujemy widok article_save (do dodawania i edycji) deklarujemy zmienne (errors, data i title). Sprawdzamy czy wysłano formularz, jeśli tak, do zmiennej article przypisujemy model Article, a do zmiennej save_errors wynik działania funkcji save_data tego modelu (ewentualne błędy). Jako parametr podajemy zmienne wysłane postem

$article=ORM::factory('article');
$save_errors=$article->save_data($_POST);

Sprawdzamy czy są błędy. Jeśli są przesyłamy z powrotem wysłane formularzem dane i błędy validacji. Jeśli nie ma błędów (wpis został dodany/zaktualizowany) przekierowujemy na stronę z artykułami.

if($save_errors){
                   $data=$_POST;
                   $errors=$save_errors;
                }else{
                    $this->request->redirect('article');
                }

Widok article_save

/application/views/article_save.php »

<h2><?php echo $title?></h2>
<?php echo  Form::open(Request::current(), array('class'=>'form'))?>
<fieldset class="ofh">
<legend><?php echo __('Fill the fields')?></legend>

    <?php echo Form::label('title', __('Title').': ').Form::input('title', Arr::get($data, 'title'))?> <span class="red"><?php echo Arr::get($errors, 'title');?></span><br />
    <?php echo Form::label('content', __('Content').': ',array('class'=>'fll')).Form::textarea('content', Arr::get($data, 'content'), array('cols'=>'40'))?> <span class="red"><?php echo Arr::get($errors, 'content');?></span><br />

    <?php echo Form::label('state', __('State').': ').Form::select('state', array('0'=>__('Unactive'), '1'=>__('Active')), Arr::get($data, 'state'))?> <span class="red"><?php echo Arr::get($errors, 'state');?></span><br />
</fieldset>
<?php echo Form::submit('submit', __('Save')).Form::close();?><br />

Zawiera formularz który wysyła dane do tej samej akcji dzięki

<?php echo  Form::open(Request::current()

Domyślnie nie ma uzupełnionych pól i błędów, ale jak je prześlemy to zostaną wyświetlone. Nie musimy sprawdzać czy zmienna w tablicy jest zadeklarowana (nie wyświetli się błąd, że nie ma takiej zmiennej, nawet jeśli jej nie prześlemy) dzięki

echo Arr::get();

Akcja edit jest podobna do akcji dodawania jednak są pewne różnice. Najpierw sprawdzamy czy użytkownik jest zalogowany. Następnie pobieramy nr id artykułu z linku

$id=$this->request->param('id');

Przypisujemy artykuł do zmiennej article

$article=ORM::factory('article',$id);

Sprawdzamy prawa: czy zalogowany użytkownik to właściciel artykułu lub administrator

if($article->user_id==Auth::instance()->get_user() || Auth::instance()->logged_in('admin')){

Jeśli ten warunek nie zostanie spełniony zostaje zmieniony tytuł i zawartość strony na brak dostępu:

}else{
                $this->template->title = __('Access error');
                $this->template->content='<h2 class="red">'.__('Access error').'</h2>';
            }

Jeśli warunek jest spełniony wczytujemy widok article_save, ustawiamy zmienne (errors, data i title). Następnie sprawdzamy czy wysłano formularz. Jeśli nie zostają przesłane dane z bazy do formularza

}else{
                    $data=$article->as_array();
                }

Jeśli wysłano formularz (jest zmienna _post submit), to do zmiennej save_errors przypisujemy wynik działania funkcji save_data. Parametr pierwszy to dane wysłane postem, drugi oznacza, że jest to edycja (nie aktualizujemy użytkownika, który edytuje artykuł, bo może to być admin). Jeśli validacja nie przeszła i nie zaktualizowano artykułu przesyłamy komunikaty błędów i zmienne z posta, jeśli nie ma błędów (poprawnie zaktualizowano) przekierowujemy na stronę z artykułami.

Akcja show służy do wyświetlenia danego artykułu. Pobieramy z linku id artykułu. Przypisujemy do zmiennej article obiekt konkretnego artykułu

$article=ORM::factory('article',$id);

Sprawdzamy czy artykuł ma aktywny status, jeśli nie wyświetlamy brak dostępu, jeśli ma aktywny, wczytujemy widok i przesyłamy zmienne (article i title).

Widok article_show

/application/views/article_show.php »

<h2><?php echo $title?></h2>
<p>
    <?php echo __('Author').': '.HTML::anchor('user/show/'.$article->user_id, $article->user->username)?><br />
    <?php echo __('Date').': '.$article->date?><br /><?php echo ($article->user_id==Auth::instance()->get_user() || Auth::instance()->logged_in('admin')) ? HTML::anchor('article/edit/'.$article->id, __('Edit')) : ''?>
</p>
<p><?php echo $article->content?></p>

Widok ten wyświetla dany artykuł. Jeśli jesteśmy właścicielem bądź adminem wyświetlany jest także link do edycji.

Akcja user służy do wyświetlania wszystkich aktywnych artykułów użytkownika. Pobieramy z linku id użytkownika, wczytujemy widok article_user i przesyłamy do niego artykuły

$articles=ORM::factory('article')->where('user_id','=',$id)->and_where('state', '=', 1)->find_all();

Uff, to wszystko!

Mogłem zrobić kompletną obsługę tego szablonu: dodawanie newsów, wyświetlanie ich na stronie, dodawanie ostatnich projektów i usług, aczkolwiek obsługa ich nie różniłaby się wiele od artykułów, wpis byłby o wiele dłuższy, a tak możecie popróbować sami, w analogiczny sposób :D

Myślę że stworzona aplikacja zawiera dużą dawkę podstawowej wiedzy, a zapoznanie się z aplikacją i dogłębne jej przeanalizowanie, ułatwi stawiać pierwsze (duże) kroki w najnowszej Konanie (3.2). Jeśli na temat danego zagadnienia jest tu coś słabo wytłumaczone, odsyłam do wcześniejszych wpisów.

Pobieranie

Aplikację można pobrać: Podstawy Kohany 3.2 (1185)
Aplikację z frameworkiem (KO3.2) można pobrać: Podstawy Kohany 3.2 wfw (1369)
W archiwach znajduje się plik z kodem sql (kohana.sql) do utworzenia bazy.

116 Odpowiedzi :“Drugie starcie – zebrana wiedza [KO3.2]”

  1. white napisał:

    dzięki takim wpisom można okiełznać kohane,
    tak trzymaj ;)

  2. proton napisał:

    Świetny wpis, ale kilka uwag:

    1. Zapomniałeś napisać o configu dla bazy danych (config/database.php).
    2. Dobrze abyś lepiej opisał kontroler auth, chodzi mi tu o akcje rejestracji przede wszystkim.

    No i czekam przede wszystkim na jakiś tutorial o modułach, przede wszystkim jakiś admin panel. ;)

    • Mariusz napisał:

      Ad.1. No tak, należy skopiować konfig database z /modules/database/config/database.php do /application/config/ i ustawić dane do połączenia (host, nazwę bazy, użytkownika, hasło)
      Ad.2 Trochę dopisałem we wpisie, jakby coś było niezrozumiałe to pisać co konkretnie jest niejasne
      O panelu admina też na pewno się coś pojawi.

  3. Destrix napisał:

    Świetny artykuł, wreszcie zrozumiałem Validację. :) Przyczepiłbym się jednak do kilku drobnych rzeczy:
    1. Dużo literówek w opisach, jak sam przeczytasz całość to wyłapiesz za jednym razem :)
    2. zamiast używać i lepiej użyć:
    i
    lepiej wygląda i bardziej przejrzyste.
    3. Bardzo ściśnięty przez co nieczytelny kod. Zawsze musze porobić dużo wcięć i rozbić :)
    4. Wydaje mi się, że add role stosujemy po save(), nie przed i nie trzeba wtedy ponownie wywoływać zapisania (potwierdzi ktoś?)

    Ale tak jak pisałem, to szczegóły, a całą Twoją pracę doceniam i wielki PLUS dla Ciebie! bardzo pomocne rzeczy.

    • Mariusz napisał:

      Ad.1. Przeczytałem i jak wyłapałem poprawiłem
      Ad.2. Też poprawiłem
      Ad.3. Kod kopiowałem z NetBeansa, wcięcia zostały, ale pojedyncze tabulatory powinny być.
      Ad.4. Też powinno działać

  4. proton napisał:

    Mógłbyś coś powiedzieć o paginacji w Kohana 3.2? Wiem, że domyślny moduł zostal usunięty, czy jest coś co mogłoby to zastąpić czy zostaje pisanie czegoś samemu?

  5. Mariusz napisał:

    Paginacja jest teraz modułem niestandardowym. W aplikacji, w której wykonuję obecnie update do KO3.2 nie działa moduł ze starszej wersji. Nie wgłębiałem się się co jest tego przyczyną. Coś niebawem napiszę o paginacji..

    • Destrix napisał:

      no to czekamy ;)

    • proton napisał:

      W sumie wykombinowałem już jakiś własny moduł, nie problem taki napisać, zastanawiają mnie teraz tylko kwestie bezpieczeństwa, np. jak zabezpieczyć aby zmienna get wskazująca na numer strony przyjmowała tylko wartości liczbowe

    • Mariusz napisał:

      Po pobraniu zmiennej z _get można np. wrzucić w funkcje php intval($var); Zamienia ona wartość na typ intiger.

  6. Destrix napisał:

    apropo Validation, to rules nie lepiej zapisać w modelu w funkcji rules() ?

    public function rules()
        {
            return array(
                'username' => array(
                    array('not_empty'),
                    array('min_length', array(':value', 4)),
                    array('max_length', array(':value', 32)),
                    array(array($this, 'username_available')),
                ),
                'password' => array(
                    array('not_empty'),
                ),
            );
        }
    

    Druga sprawa to jak na wprowadzanych danych gdy tworzymy nowy artykuł wykonać funkcję php trim itp? Validation już nie obsługuje filters() od kohany 3.1 bodajże.

  7. Mariusz napisał:

    Taka funkcja rules() jest już w modelu user (/modules/orm/classes/model/auth/user.php) dlatego przypisuję jej wynik do

    $rules=  Model_User::factory('user')->rules();
    

    Tak, filtry zostały usunięte z validacji. Było to tłumaczone tym, że validacja nie powinna zmieniać zawartości validowanych zmiennych. Filtry należałoby zastosować tuż przed zapisaniem do bazy.

  8. Mariusz napisał:

    Zauważyłem, że komunikaty przy validacji w języku en nie były tłumaczone dla błędu unique (występuje to w angielskiej wersji jak chcemy dodać email/username który jest już w bazie). Aby dobrze tłumaczył należy:
    1. Usunąć plik en.php z /i18n
    2. Stworzyć plik messages.php w /messages o treści

    <?php defined('SYSPATH') OR die('No direct access allowed.');
    
    return array(
        'email' => array(
                'unique'   => ':field must be unique',
        ),
        'username' => array(
                'unique'   => ':field must be unique',
        ),
    );
    ?>
    

    3. Poprawić kontroler Auth, w miejscu wysyłania błędów

    $errors=$post->errors('register');

    Zamienić na

    $errors=$post->errors('messages');

    4. Zaktualizować pl język w pliku pl.php w folderze /i18n

    'register.email.unique'=>':field musi być unikalny',
    'register.username.unique'=>':field musi być unikalny',
    

    Zamienić na:

    ':field must be unique'=>':field musi być unikalny',
    
    • Mariusz napisał:

      messages.php nie powinien zwracać tablic dla każdego pola, tylko:

      <?php defined('SYSPATH') OR die('No direct access allowed.');
       
      return array(
                  'unique'   => ':field must be unique',
      );
      ?>
      
  9. chomi3 napisał:

    czesc Macku!
    Na wstepie – swietna robota!
    Bardzo sie ciesze ze udostepniles druga zakladke od lewej, nalezy Ci sie :)

    Mam pytanie, nie moge sie nigdzie dokopac do tego dlaczego w skladni jest „n”:

    $type) echo HTML::style($file, array(‚media’ => $type)), „n” ?>

    Ktore mi wypluwa sie na samej gorze strony.

    Oczywiscie usuniecie tego parametru sprawia ze wszystko dziala przeslicznie, ale po co to wlasciwie jest?

    Z gory dzieki za odpowiedz.
    pzdr.

    • Mariusz napisał:

      Dzięki za podarunek :D
      Powinno być \n, wordpress usunął backslash z treści. Powoduje to złamanie linii dla wczytywanych stylów (jak popatrzymy w źródło strony). Bez tego również może być.

  10. white napisał:

    czesc ponowanie :P

    a ja mam pytanie odnośnie netbeans,
    Jak zaczynasz nowy projekt ?
    jest jakiś myk, niż wklejanie plików i tworzenie projektu z istniejącymi plikami ?

    • Mariusz napisał:

      Aby podczas tworzenia nowego projektu na końcu wyświetlał framework Kohana do wyboru, trzeba by NetBeansa dostosować. Ja jak tworzę nowy projekt, to podaję nazwę, wskazuję folder, w którym jest już Kohana, zaznaczam, żeby pliki netbeansowe trzymał w innym katalogu, tworzę .htaccess z example.htaccess i ustawiam prawa do cache i logs.

  11. chomi3 napisał:

    hej,

    mam jeszcze pytanie odnosnie sesji, gdzie jest ona inicjowana?

    proba rejestracji usera konczy sie:

    322 		catch (Exception $e)
    323 		{
    324 			// Error reading the session, usually
    325 			// a corrupt session.
    326 			throw new Session_Exception('Error reading session data.', NULL, Session_Exception::SESSION_CORRUPT);
    327 		}
    328 
    329 		if (is_array($data))
    330 		{
    331 			// Load the data locally
    
    • Mariusz napisał:

      Sesja jest inicjowana przez moduł Auth. W configu jest deklaracja

      'session_type' => Session::$default,
      

      Spróbuj się zalogować na admin admin lub user user.

  12. tojach napisał:

    mam taki błąd w tej linijce:

    $client->add(‚roles’, ORM::factory(‚role’, 1));

    ErrorException [ Notice ]: Undefined index: roles

    Powodem może być pusta klasa Model_Role?

    • Mariusz napisał:

      Widzę, że coś zmieniałeś, czy wcześniej też nie działało? Co więcej zmieniłeś? Model Role i User jest w module ORM, wiec nie musi być w /application. Nie nadpisałeś go czasem?

      • tojach napisał:

        Ogolnie to posiłkowalem sie twoim tutkiem w moim projekcie, ale jesli chodzi o rejestracje uzytkownikow to niewiele zmienialem. u mnie wyglada to tak:

                    $client = ORM::factory('user');
                    $client->email = $_POST['email'];
                    $client->username = $_POST['username'];
                    $client->password = $_POST['password'];     
                    
                    $client->add('roles', ORM::factory('role', 1)); 
                    $client->save();
        

        Poprostu tak jakby nie łapało tego ‚roles’. Dla proby zmienialem ‚roles’ na inną tabele w bazie (tags) i zaden blad nie wyskoczyl ale tez sie nic nie stało. Probowalem tez dac save() przed, po add() i oba naraz. Jakkolwiek bym nie uzyl funkcji add() z aliasem ‚roles’ to mi nie działa. Coś moze nie tak z bazą, chociaż w bazie mam raczej wszystko wporzadku, bo tworzyłem tabele wg. tutorialu kohany. Role mam dodane dwie: login, admin.

        Nie wiem co jeszcze mógłbym napisac by pomoc wam w pomaganiu mi rozwiazac ten problem :P

      • Mariusz napisał:

        A relacje też w bazie utworzyłeś? Rozwiń, skopiuj, wklej wszystkie błędy na pastebin.com i daj do nich linka.

        • tojach napisał:

          Relacji w bazie żadnych nie robiłem, bo szczerze mowiac to i nie wiem jak. Być może to jest problem, bo reszta wydaje się być poprawna. Jak możesz to napisz co gdzie dokleić aby te relacje dzialaly ;p

          Tutaj link z błędami:
          http://pastebin.com/ppAzPFW7

  13. tojach napisał:

    zdaje sie ze o to chodzi

    ALTER TABLE `roles_users`
      ADD CONSTRAINT `roles_users_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
      ADD CONSTRAINT `roles_users_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE;
    
    ALTER TABLE `user_tokens`
      ADD CONSTRAINT `user_tokens_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE;
    

    jeśli tak to dodałem to, bo pochodzi to z pliku auth-schema-mysql.sql z ktorego tworzyłem tabele. Mam jedno zastrzeżenie co do nazwy ‚`user_tokens_ibfk_1`. Potrzebna jest ta końcówka _ibfk_1 ?

    • Mariusz napisał:

      Tak ma być. Z błędów jakby z bazą coś nie tak (relacjami). Spróbuj utworzyć drugą bazę i zaimportuj cały plik sql w phpmyadmin, lub podaj ścieżkę (source sciezka/do/pliku.sql;) w konsoli mysql. Zmień dane w configu do bazy i zobacz czy na nowej też ten problem występuje. Ewentualnie skopiuj część, która jest po walidacji – zapisuje usera i role do bazy, z przykładu tutaj.
      Może rozszerzając model User nadpisujesz $_has_many ?

      • tojach napisał:

        Dzięki, działa :) dzieki twojej podpowiedzi o tym ze nadpisuje $_has_many udalo mi sie rozwiazac problem :) a meczylem sie z tym od wczoraj. Musiałem dokleić $_has_many z Model_User (z Auth) do mojego Model_User.
        Jeszce powiedz mi jedno.

        class Model_User extends ORM {
              protected $_has_many = '...';
        
              public function rules()
              {
                 //...
              }
        }
        

        To powyżej jak rozumiem jest nadpisanie Model_User z Auth. Czy da sie zrobić model który rozszerza Model_User (z Auth) i jednoczesnie ma funkcje ORM (zebym mogl korzystac z bazy).

        I czy jakbym zostawił w takim stanie jak jest (czyli goły Model_User z rules i $_has_many) to czy byłoby to poprawne? Chodzi mi czy nie zniknęły by mi jakies ważne funkcje :)

      • Mariusz napisał:

        Jeśli chcemy rozszerzyć lub nadpisać model User to w /application/user tworzymy user.php

        <?php defined('SYSPATH') or die('No direct access allowed.');
        
        class Model_User extends Model_Auth_User {
        
        	// This class can be replaced or extended
        
        } // End User Model
        

        i tam zmieniamy. Jeśli chcemy coś dodać do jakiejś funkcji, to ją nadpisujemy – należy ją skopiować z modelu i umieścić ze zmianami w stworzonym pliku. Jeśli dodamy jakąś nową funkcję, to nie ma wpływu na pozostałe (chyba że wykorzystujemy inne funkcje z modelu w niej).
        Najpierw przeszukiwane są pliki z /application, jak nie znajdzie klasy, czy funkcji to z /modules, a jak dalej nie znajdzie to z /system.

        • tojach napisał:

          Dzięki jakoś sobie poradziłem. Chciałbym jeszcze wiedzieć jak dodać do walidacji regułe o unikalnym kluczu, żeby zwracało błąd gdy powtórzy sie w bazie ‚username’. Szperalem po necie, ale tylko do starszych wersji kohany coś można znaleść.

        • Mariusz napisał:

          W modelu User są już takie reguły, które odpowiadają za unikalność username i email. Wystarczy je pobrać i dodać do reguł

          $rules=  Model_User::factory('user')->rules();
              $post = Validation::factory($_POST)
                  ->rules('email', $rules['email'])
                  ->rules('username', $rules['username'])
          
  14. tojach napisał:

    Reguły już ogarnąłem. Wielkie dzięki za pomoc. Nastepne pytanko… Jak to jest z rolami? Czy każdy użytkownik musi mieć role ‚login’ żeby się zalogować? zrobiłem konto admina i gdy ma dwie role (login, admin) to może sie logować, a jak samą role ‚admin’ to już nie.

    • Mariusz napisał:

      Tak, musi mieć wszystkie role (login, admin i jak dodasz jakieś inne to też). Polecam przeczytać wpis: Logowanie z Auth, który znajduje się na tym blogu :)

  15. tojach napisał:

    Pytanie o artykuły:
    Gdybym chciał dodać do artykułów jeszcze date utworzenia to wystarczy utworzyć pole ‚date’ w tabeli ‚articles’ i przypisać time() podczas dodawania? Co musiałbym zatem zrobić by je wyświetlić wg. daty.

    ORM::factory('article')->where('state','=',1)->find_all();
    

    Czy to zwróci artykuły od najnowszego?
    I jeszcze jedna sprawa, a w zasadzie prośba. Czy mógłbyś w przyszłości napisać coś o paginacji, lub wskazać jakiś tutek do tego, bo paginacja w kohanie zdaje sie nie działać o wersji 3.0.

    PS: nie jestem pewien czy tu jest błąd.

    if($article->user_id==Auth::instance()->get_user() || Auth::instance()->logged_in('admin'))
    

    ale czy nie powinno być:

    Auth::instance()->get_user()->username
    
    • Mariusz napisał:

      Data utworzenia jest już w artykułach, można by dodać datę edycji. Aby sortowało wg najnowszego należy dodać

      ->order_by('date','desc')
      

      oznacza to sortowanie wg kolumny o nazwie date malejąco

      ORM::factory('article')->where('state','=',1)->order_by('date','desc')->find_all();
      

      O paginacji coś się pojawi niebawem. A paginację z KO3 poprawiliśmy ze znajomym tak naokoło – aby działała w KO3.2, bo zrezygnowano z pewnych mechanizmów…

      if($article->user_id==Auth::instance()->get_user() || Auth::instance()->logged_in(‘admin’))
      

      Warunek ten sprawdza, czy id użytkownika, który dodał artykuł jest równe z id zalogowanego, lub rola zalogowanego równa się admin. Nie ma tu błędu

  16. Aylard napisał:

    Pytanko – wrzuciłem Kohanę do podkatalogu serwera, zmieniłem w bootstrap base url na ‚/podkatalog/’ i strona się uruchomiła, jednak gdy mam ‚example.com/dir/controller’ nie wyświetla treści, tylko pokazuje sie ‚example.com’ jednak pod URL ‚example.com/dir/controller’ (nie przekierowuje lub maskuje adres)

    • Mariusz napisał:

      W pliku .htaccess też trzeba zmienić

      # Installation directory
      RewriteBase /dir
      

      W szablonie nie zmieniałem odwołań obrazków na HTML::images() stąd zapewne nie będzie wyświetlać obrazków, trzeba by poprawić ew. dodać URL::base();

  17. Witam,

    mam pytanie odnośnie błedów przy sprawdzaniu formularza przy pomocy helpera Validation, jak uzyskać komunikaty o błędach i je jakoś wyświetlić przyzwoicie dla użytkownika ?

  18. Mariusz napisał:

    Ogólnie to wygląda tak: po wysłaniu formularza sprawdzamy czy walidacja przechodzi, jak ok to zapisujemy, jak nie to przesyłamy do formularza wysłane dane (aby nie trzeba ponownie wszystkiego wpisywać) i komunikaty błędów.

    $valid=  Validation::factory($_POST)
                ->rules('title', array(array('not_empty'),array('min_length', array(':value', 4))))
                ->rules('content', array(array('not_empty'),array('min_length', array(':value', 100))))
                ->rule('state', 'digit')
                ->labels(array('title'=>'Title','content'=>'Content','state'=>'State'));
            if($valid->check()){
                //save();
            }else{
                $this->template->errors=$valid->errors('article');
                $this->template->data=$_POST;
            }
    

    A w widoku

    <?php echo Form::label('title', __('Title').': ').Form::input('title', Arr::get($data, 'title'))?> <span class="red"><?php echo Arr::get($errors, 'title');?></span><br />
    

    Defaultowe, angielskie komunikaty znajdują się w /system/messages/validation.php
    Przetłumaczone dodałem do katalogu /application/i18n, można dodać także własne do katalogu/application/messages, i podać nazwę pliku

    $this->template->errors=$valid->errors('nazwa_pliku');
    
  19. Witam,
    super, że ktoś zajmuje się opisywaniem Kohany, bo ciężko cokolwiek znaleźć w sieci. Ja mam takie pytanie. Jak przekazać do viewsa ‚home’ jakieś zmienne? Logiczne wydaje mi się coś takiego:

    public function action_index() {
            $this->template->content=View::factory('home');
            $this->template->content->zmienna = 'wartosc';
            $this->template->title = __('Home');
            $this->template->top_tab='home';
        }
    

    W Kohanie 3.0.x to działało, natomiast w Kohanie 3.2.x dostaję komunikat, że zmienna nie jest zdefiniowana. Może wiesz jak to się robi w Kohanie 3.2.x? Byłbym wdzięczny za pomoc.

    • Mariusz napisał:

      Nie było zmiany w Ko3.2 Dobrze przekazujesz. Może nie do podwidoku home, tylko do głównego widoku- szablonu (bez content po template)?

  20. Dlaczego używasz $_COOKIE, setcookie itd? Kohana ma od tego specjalną klasę (http://kohanaframework.org/3.2/guide/api/Cookie)

    • Mariusz napisał:

      Czasami zdarza mi się nie wykorzystywać wszystkich dobrodziejstw Kohany. Oczywiście można tutaj użyć klasę Cookie do wykrywania i ustawiania języka.

  21. Czy mozna stworzyc jakis podstawowy kontroler, ktory uruchomi sie na kazdej podstronie? Np. chce miec $this->template->auth=Auth::instance()->get_user(); – taki skrot. Probowalem to wsadzic do action_before() w Controller_Default, ktory jest rozbudowywany, przez wszystkie inne kontrolery jakie mam na stronie, ale niestety to sie nie sprawdza. Tak samo np. chcialbym dac $top_tab=NULL; na samym poczatku, zeby mi nie wywalalo bledu, jak nie wpisze wartosci do top_tab.

    Ogolnie wpis fajny, ale mozna odniesc wrazenie, ze za malo laduje w modelu. Dla mnie logiczne jest, ze cala walidacje robie wlasnie tam.

    • Mariusz napisał:

      Kontroler który uruchamia się na każdej str. to tutaj Default.
      Możesz np. zadeklarować atrybut w klasie Default (tak jak zmienną template):

      public $user;
      

      W konstruktorze przypisać wartość:

      $this->user=Auth::instance()->get_user();
      

      A w before przesłać do widoku, możemy także przesłać do głównego widoku top_tab (to jest w tym poradniku wysyłane):

      $this->template->user=$this->user;
      $this->template->top_tab='';
      

      Aby odwołać się w innym kontrolerze do usera wykorzystujemy

      $this->user
      

      Co do modelu, to tutaj nie ma zbyt dużo rzeczy dodawanych do bazy (artykuły i rejestracja). Walidacja artykułów jest w modelu, a model user jest w auth.

  22. taj napisał:

    Dzieki za opdowiedz:) Byc moze pomozesz mi w jeszcze jednej kwestii – mam tabelke z userami, nad tabelka chce wyswietlic liste grup(roles) do filtrowania userow. Dalem

    Form::select('groups', ORM::factory('role')->find_all()->as_array())
    

    i uzyskalem w latwy sposob prawie zadowalajacy efekt, tylko, ze wyswietla mi sie id roli – mozna jakos przymusic kohane, aby pobrala jedynie nazwy, albo aby jedynie nazwy wyswietlila?

    • Mariusz napisał:

      Aby nazwa była indexem to:

      as_array('name');
      

      Albo tak jak teraz masz zmienną np. $role to można też przy wyświetlaniu tablicy dać

      $role['name'];
      

      Polecam też debuga, aby sprawdzić co jest pod zmienną (obiekt/tablica z jakimi wartościami). Po pobraniu ORMem i przypisaniu do zmiennej wyświetl echo

      echo Debug::vars($roles);
      
  23. morawcik napisał:

    Mógłby mi ktoś wyjaśnić jak działa logowanie/rejestracja ? Chodzi mi o sposób hashowania hasła.

  24. Mariusz napisał:

    Jak wiadomo hashowanie odbywa się w jedną stronę, czy to md5 czy sha z ciągu znaków tworzy się hash, który podczas rejestracji jest zapisywany do bazy, a podczas logowania sprawdzany czy zgadza się z tym w bazie. Nie ma możliwości rozszyfrowania hasła z hashu (chyba że mamy do dyspozycji superkomputery).
    Zastosowanie dodatkowego hasha mieszającego ma na celu skomplikowanie ciągu wejściowego, z którego tworzony jest ostateczny hash.
    Dokładnie się nie wgłębiałem jak to działa (trzeba popatrzyć na moduł Auth lub ORM), ale wydaje mi się, że wprowadzone w input hasło i hasha z configu dodatkowo dzieli się i przestawia w odpowiedni sposób i na podstawie tych poprzestawianych części tworzy się funkcją hashującą (md5, sha256) hash hasła.

    • morawcik napisał:

      To teraz już rozumiem, najpierw zwykły hash (md5 czy sha256) a potem hash z kluczem. Nie mogłem dojść do tego dlaczego hasło po rejestracji jest inne niż wszystkie możliwości z generatora hash.

      Możesz mi jeszcze podać przykładowy (gotowy) plik /application/config/auth.php bo nie bardzo rozumiem jak mam go zmienić.

    • Mariusz napisał:

      Czy najpierw hashem klucz czy funkcją hashującą (md5, sha256) to już by trzeba się zagłębić w moduł. Dodatkowe hashowanie czy mieszanie jest np. po to ponieważ na internecie są bazy hashów i odpowiadającym im wartości. Porównując hash przechwycony z bazy możemy otrzymać hasło. Zastosowanie dodatkowego hasha komplikuje to, a jeśli jeszcze występuje mieszanie to jeszcze bardziej i jeśli nie jesteśmy w posiadaniu tajnego hasha klucza i algorytmu mieszania to rozkodowanie jest praktycznie niemożliwe.

      Config znajduje się w tym wpisie, należy tylko podmienić własny hash klucz, tu jest funkcja którą możemy taki hash utworzyć -hash_hmac() (w configu jej nie musi być, a jedynie jej wynik obliczony wcześniej).

  25. Rafał napisał:

    mam dwa pytanie odnośnie tego nie powiem dobrze napisanego kursu
    1. jakbym chciał mieć dostęp do całej strony tylko dla zalogowanych to czy muszę w każdej metodzie sprawdzać czy ktoś jest zalogowany czy w klasie defoult można to wpisać np w before ?
    2. jak już mam zrobiony dostęp do strony tylko dla zalogowanych to jak wtedy wygląda sprawa szablonu, który się u ciebie cały ładuje za każdym razem i się zmieniają tylko małe kawałki. czy w przypadku logowania wystarczy zmienną template nadpisać nazwa innego szablonu ?

    Z góry dzięki za odpowiedź

    • Mariusz napisał:

      1. Nie musisz, można globalnie sprawdzać, np. w konstruktorze danego kontrolera czy w defaultowym kontrolerze.

      2. Można podmienić template w jakimś kontrolerze:

      class Controller_New extends Controller_Default {
          public $template='new_default';
      

      Można także widok template zmienić na następujący:
      /application/views/empty.php

      <?php echo $content; ?>
      

      Załadować go do template:

      class Controller_Default extends Controller_Template {
          public $template = 'empty';
      

      A później sprawdzać czy jesteśmy zalogowani czy nie i ładować odpowiednie widoki:

      if (Auth::instance()->logged_in())
      $this->template->content='template_login';
      else
      $this->template->content='template_guest';
      

      Widoki to już wedle uznania, np. w template_guest formularz logowania, a w template_login właściwa zawartość.

  26. Dominik napisał:

    witam!
    a co zrobić zeby działało zamiast
    localhost/index.php/home/index, działało
    localhost/home/index.

    bo jeśli dobrze zrozumiałem to

    Kohana::init(array(
        'base_url'   => '/',
            'index_file' => FALSE,
    ));
    

    nie działa.

    • Mariusz napisał:

      A czy z example.htaccess utworzyłeś .htaccess? Potrzebny jest także moduł mod_rewrite apache. Ew. popatrz w plik .htaccess (w głównym folderze, może być ukryty) i zobacz czy masz linię:

      RewriteRule .* index.php/$0 [PT]
      
  27. Dominik napisał:

    no działa ale jeszcze mam pytanie odnośnie takiego czegoś, ze co jeśli stronę mam na localhost/strona/

    wtedy na tym skrypcie gdy chcę się zalogować przekierowuje mnie do panelu xampp. ;/

    • Mariusz napisał:

      Przy tworzeniu poradnika nie pomyślałem, aby działał również w podkatalogach i np. ścieżki do img podawałem ręcznie, albo przekierowania po wykonaniu operacji.. :(

      Kilka komentarzy wyżej @Aylard pytał o podobną rzecz.

      Jeśli popatrzysz w kontroler Auth to zauważysz, że po zalogowaniu przekierowuje na stronę /user (zapewne localhost/user masz xampp).

      $this->request->redirect('user');
      

      Należałoby zmienić i wykorzystać URL::base(); aby adres pobierało prawidłowo również w przypadku podkatalogu, to samo tyczy się obrazków (można wykorzystać HTML::image();)

      $this->request->redirect(URL::base().'user');
      

      Jeśli już mowa o URL::base(), to zwraca ona z configu link, np. /dir/ Można dodać do niej 2 parametry, pierwszy to protokół i podanie go oznacza, że zwróci cały adres: http://localhost/dir/, a w drugim możemy wyłączyć wyświetlanie index.php, ma on sens jeśli w configu nie ma ‚index_file’ => FALSE,

      Przy ‚trzecie starcie’ postaram się zrobić uniwersalnie.

  28. kmf napisał:

    Hej
    Świetny blog, bardzo przydatne artykuły :) Czy można się spodziewać czegoś o paginacji w Kohanie 3.2?

    • Mariusz napisał:

      Postaram się opisać jak przerobić obecną (aby działała w KO3.2), albo stworzyć własny helper oparty o model.

  29. Rafał napisał:

    Witam,

    Kawał dobrej roboty odwalasz tutaj na blogu!

    Mam kilka pytań:
    1. Próbowałem zaimplementować do „Drugiego starcia” ajax’a, niestety z mizernym skutkiem. Korzystałem z kodu tego autora https://github.com/badsyntax/kohana3-examples. Jedynie co udało mi się uzyskać to wyświetlanie całej strony w miejscu wskazanym. Czy w najbliższym czasie przewidujesz jakiś artykuł poświęcony temu zagadnieniu? Fajnie jakby w systemie była możliwość włączania/wyłączania ajax’a(jeśli to możliwe). Ewentualnie jakbyś mógł wskazać jakieś źródła?
    2. Czytałem jakiś czas temu wypowiedź, wydawałoby się profesjonalisty, który napisał, że profesjonalnym rozwiązaniem byłoby podpięcie doctrine zamiast ORM’a Kohany. Jeśli to fakt to wiesz może jaką przewagę i w czym jest lepszy ten doctrine?
    3. Co do wypowiedzi d4rky z 28 października 2011 at 20:56. To próbowałem użyć Cookie::set();, na początku miałem problemy z ustawiem „salt”, jak już mi się udało to nie wczytywał mi się język. Zauważyłem, że do wartości cook’ie dodawany jest jakiś hash. Mógłbyś pokazać przykładową implementacje Cookie::set();?

    • Mariusz napisał:

      Ad.1. Zależy do czego ten Ajax. Jeśli ma być to np. do pobierania dodatkowej treści jak na 9lessons, to możesz zrobić kontroler Ajax i kodem js jak na tej stronie

      class Controller_Ajax extends Controller{
        public function action_get_sth(){
          //tresc do pobrania z bazy i wyświetlenia echo (można też json)
        }
      }
      

      A wtedy w tym kodzie java script z linku co podałem:

      url: "/ajax/get_sth",
      

      Jeśli przesyłamy coś postem czy getem, to należy zabezpieczyć np. deklarując typ, dodając shashe czy escapując stringi:

      $sth=(int)$this->request->param('id');
      $sth2=addslashes($_POST['sht2']);
      $sth3=mysql_real_escape_string($_POST['sht3']);
      

      Ad.2. Ogólnie z ORMem (Mapowaniem obiektowo-relacyjnym) związany jest szereg problemów wydajnościowych. Jednakże bardzo ułatwia pisanie aplikacji i komunikację z bazą. Jeśli robimy strony małe i średnie to jest OK. Jeśli chcielibyśmy zrobić duży portal (np. społecznościowy) z bardzo dużą liczbą użytkowników online, to można zamiast ORM wykorzystać DB (również moduł Kohany). Przeprowadzaliśmy swego czasu testy i czas wykonywania DB był o połowę krótszy, niż ORM, jednakże każde zapytanie musimy pisać ręcznie i nie relacje musimy sami pobierać.

      Ad.3. Może dokumentacja coś pomoże.

  30. Dominik napisał:

    dzięki za wszystkie odpowiedzi ale mam jeszcze jedno chyba ostatnie pytanie.

    kontroler user, akcja show.
    $id=$this->request->param(‚id’);
    powinna popobrać ID zalogowanego usera z bazy danych?
    mi nie działa bo wtedy nie wyświetla danych w user/show

    zamiast tego użyłem
    $id=ORM::factory(‚user’, Auth::instance()->get_user());
    czy ten kod bedzie rownież poprawny?

    • judasz napisał:

      witam świetny wpis. mam nadzieje ze będzie takich więcej.
      mam pytanie, jeśli zmienię nazwę tabeli np z surname na nick to jakie modyfikacje należy wprowadzić w akcji auth/register
      bo edycja tej linijki nie diała
      $client->username = $_POST['username'];

      • Mariusz napisał:

        Chodzi o kolumnę username w tabeli users zapewne. Spróbuj:

        $client->nick = $_POST['username'];
        

        Bo inaczej nie ma inputa nick, chyba że w widoku zmieniłeś.
        Jednakże w modułu Auth pole to ma nazwę username i nie będzie validacji, np. sprawdzania unikalności nicku. Modułu raczej bym nie edytował, bo jak zaktualizujesz framework, to zmiany się usuną, chyba że nadpiszesz umieszczając w katalogu application pliki wymagające poprawy (tak można). Jeśli jest to możliwe to zostawiłbym taką nazwę w bazie, ew. przy wyświetlaniu zmienić.

    • Mariusz napisał:
      $id=$this->request->param(‘id’);
      

      Przypisuje do zmiennej id wartość parametru z linku localhost/kontroler/akcja/parametr. A więc więc pokazywało to dane użytkownika, którego id był podawany w linku. Dane po zalogowaniu były zapewne w akcji index. Aby pobrać id użytkownika dajemy samo

      $user=Auth::instance()->get_user();
      

      Moduł Auth wykorzystuje ORM, więc alby pobrać dane dajemy później:

      $user->username;
      

      Nie potrzeba zatem drugi raz ORMem wyciągać danych użytkownika, jeśli jednak id jest z linku, to OK.

  31. janic napisał:

    Witam,

    Mam pytanie odnośnie Gmaps, aby zadziałało trzeba jakiś config zmienić?

    Pozdrawiam

    • Mariusz napisał:

      W pliku /modules/gmaps/classes/kohana/gmaps.php zmienić odwołanie do configu (u mnie 29 linia) na

      $config = Kohana::$config->load('gmaps');
      

      Ale to tylko jeśli moduł był kopiowany osobno (archiwum bez frameworka).

  32. Witam mam problem chyba od strony serwera ponieważ budując według waszych wpisów kontrolery linki na stronie nie przerzucają mnie na odpowiednie podstrony
    Co może być tego przyczyną ???

    • Mariusz napisał:

      Moduł rewrite nie włączony, opcja AllowOverride All nie zmieniona do Directory hosta głównego lub vhosta. Ewentualnie spróbuj dodać do .htaccess Options FollowSymLinks

    • a jak go mam załączyć ?
      nie znak kohany i dopiero się jej uczę odłożyłem ją na kilka miesięcy ale jednak chcę nauczyć się budować na niej aplikacje internetowe

      • dodałem to do .htaccess i wyskakują mi błędy typu

        SYSPATH/classes/kohana/request/client/internal.php [ 87 ]
        82 
        83 		try
        84 		{
        85 			if ( ! class_exists($prefix.$controller))
        86 			{
        87 				throw new HTTP_Exception_404('The requested URL :uri was not found on this server.',
        88 													array(':uri' =&gt; $request-&gt;uri()));
        89 			}
        90 
        91 			// Load the controller using reflection
        92 			$class = new ReflectionClass($prefix.$controller);
        
  33. Sylwek napisał:

    Witaj,
    Super strona. Ale mam pytanie – w CMSie który chcę stworzyć chciałbym by jeśli przypiszę do artykułu datę w przyszłości (np 20.04.2012) artykuł został opublikowany automatycznie po tym jak data na serwerze gdzie stoi serwis będzie równa 20.04.2012. W cronie wiem jak to zrobić ale pytanie jak to zrobić by nie trzeba było używać crona?

    • Mariusz napisał:

      Przy dodawaniu jeśli podasz datę w przyszłości to tylko ją zapisać w bazie (jeśli nie podasz to np. aktualną datę). Potem przy wyciąganiu w zapytaniu pobierasz artykuły tylko te gdzie różnica dat jest >=0 (jeśli jest mniejsza to artykuł z przyszłości).

  34. Sylwek napisał:

    Dzięki. Spróbuję zrobić wg. twojej sugestii.

  35. Northulus napisał:

    Witam,
    pobrałem aplikację wraz z frameworkiem.
    Skonfigurowałem ją tak jak umiałem by działała pod lokalny serwer Webserv.
    Strona główna wyświetla się prawidłowo, jednak jeśli chcę przejść do jakiegokolwiek odnośnika, pokazuje się błąd:

    Kohana_Exception [ 0 ]: Attempted to load an invalid or missing module 'gmaps' at 'MODPATH\gmaps'
    

    Sprawdziłem w pliku bootstrap.php, wpis o niestandardowy moduł widnieje.
    Wchodzą w folder modules, folder gmaps również istnieje.

    Co zrobić aby moduł gmaps funkcjonował lokalnie oraz poprawnie? :)
    Pozdrawiam i proszę o poradę.

    • Mariusz napisał:

      Spróbuj zakomentować linię w bootstrap.php odpowiadającą za ładowanie modułu Gmaps (moduł ten wykorzystywany jest jedynie w kontrolerze User w akcji map):

      //'gmaps'        => MODPATH.'gmaps',        // Google Maps
      

      i zobacz czy wtedy serwer prawidłowo podąża za linkami.

    • Northulus napisał:

      Dziękuję za szybką odpowiedź!

      Niestety, próbowałem to już wcześniej, jednak to nie pomogło.

      Opiszę może dokładniej swój problem:
      Strona główna się ładuje, jednak po przejściu do jakiegokolwiek linku, pokazuje ww. błąd.

      Błąd:
      Kohana_Exception [ 0 ]: Attempted to load an invalid or missing module ‚gmaps’ at ‚MODPATH\gmaps’
      SYSPATH\classes\kohana\core.php [ 542 ]
      537 $paths[] = $modules[$name] = realpath($path).DIRECTORY_SEPARATOR;
      538 }
      539 else
      540 {
      541 // This module is invalid, remove it
      542 throw new Kohana_Exception(‚Attempted to load an invalid or missing module \’:module\’ at \’:path\”, array(
      543 ‚:module’ => $name,
      544 ‚:path’ => Debug::path($path),
      545 ));
      546 }
      547 }

      APPPATH\bootstrap.php [ 111 ] » Kohana_Core::modules(arguments)
      DOCROOT\index.php [ 102 ] » require(arguments)

      Korzystam z Webserv’a:
      PHP 5.2.5.
      Apache 2.2.6.
      MySQL 5.0.45.

      Naturalnie utworzyłem bazę danych i pozmieniałem odpowiednie wpisy.
      Dodam, że po przejściu np. do artykułów link jest prawidłowy (?), tzn.:
      http://localhost/~drugiestarcie/article

      Pozdrawiam i proszę o kolejne wskazówki.

      PS. Odpowiedziałem w odpowiedzi, jednak nie została ona wyświetlona. Jeśli jest to poprawne działanie WP, to przepraszam za kłopot. :)

  36. Northulus napisał:

    Witam ponownie!
    Problem zniknął gdy przeniosłem wszystko z lokalnego serwera na serwer online.

    Wszystkim uczącym się, polecam ten wybór, jest znacznie mniej problemowy. Zachęcam do stosowania na początek z darmowych hostingów. Prawdą jest, że zazwyczaj mamy wtedy utrudniony dostęp do bibliotek – w moim przypadku brak curl – jednak mimo wszystko – warto!

    Szacunek dla Twojej wiedzy, Mariusz.
    Świetny blog.

    • Mariusz napisał:

      Polecam robić na linuxie (np. openSUSE, linux mint), zainstalowanym na sprzęcie albo na wirtualnej maszynie. Ja tak działam i nie mam problemów, a linux nie taki straszny ;)

  37. Bartek napisał:

    co robi ponizsza linijka w kontrolerze article w akcji show?

    if($article->state){
    • Mariusz napisał:

      Sprawdza czy status artykułu ma wartość pozytywną (0 – nieaktywny, 1 – aktywny). Jeśli byłoby więcej statusów to lepiej użyć:

      if($article->state == 1){
      
  38. Bartek napisał:

    jak powinien wygladac routing dla sciezki z dwoma podfolderami: folder/folder/controller?

    • Mariusz napisał:

      Spróbuj ustawić directory folder/folder i zobacz czy zadziała:

      Route::set('folder-test', 'folder/folder(/<controller>(/<action>(/<id>))) )
          ->defaults(array(
              'directory'  => 'folder/folder',
              'controller' => 'test',
              'action'     => 'index',
      ));
      

      a może chodzi Ci o główny katalog?

      PS. Kolejność routingu ma znaczenie (wczytywany jest pierwszy dopasowany).

      • Bartek napisał:

        probowalem takie rozwiazanie lecz niesttey nie dziala.

        obecennie mam takie:

        Route::set('folder-test', 'folder(/<controller>(/<action>(/<id>))) )
            ->defaults(array(
                'directory'  => 'folder/folder',
                'controller' => 'test',
                'action'     => 'index',
        ));
        

        lecz wtedy moja siezka wyglada tak folder/controller a mi zalezy na tym zeby w sciezce pokazywaly sie nazwy obu folderow

        • Mariusz napisał:

          Zdebuguj route czy requerst, bo może routing wybrało dobry, tylko nie może odnaleźć kontrolera test w controllers/folder/folder? Spróbuj też z

          
          Route::set('folder-test', '<path>(/<controller>(/<action>(/<id>))), array('path' => 'folder/folder') )
          

          albo w defaults ustaw:

          'directory'  => 'folder_folder',
          

          Być może wtedy będzie szukało w podfolderze.

  39. Witam, mam małą prośbę czy mógłby ktoś utworzyć funkcję wysyłającą tekst z pola „zawartość” na adres mailowy np. kamil@wp..pl byłbym wdzięczny, dlatego że jestem baaaardzo początkujący i nie dam sobie rady :(

    • Mariusz napisał:

      Mógłbyś wykorzystać funkcję php mail:

      mail($to, $subject, $message);
      

      Albo wykorzystać swiftmailer

      Jak to powinno wyglądać w kontrolerze?
      1. Wczytujemy widok z formularzem.
      2. Walidujemy/Sprawdzamy czy są wszystkie pola.
      3. Jeśli są błędy (np. nie podano treści) wyświetlamy info w widoku, jeśli nie ma to wysyłamy email przy pomocy funkcji mail, albo swiftmailer, wyświetlamy info że mail wysłany.

    • Bartek napisał:

      Pozdrowienia. Uczę się kohany od początku. Z kilkoma Pańskimi projektami nie mam kłopotów, z kilkoma mam. W tym z obecnym. Ściągnąłem cały projekt, odpalam w apache na localhoście. bazę zrobiłem z pliku. zmieniłem tylko nazwę na kohanaorm, ale to nic. Stronka się odpala w przeglądarce (pomajstrowałem w bootstrapie i htaccesie podając nowe kompletne linki do obecnego folderu aplikacji) ale jak próbuje przejść po którymkolwiek linku (article, register etc) to mi się wyświetla komunikat że:
      „Zabroniony dostęp!

      Nie masz dostępu do żądanego obiektu. Jest on zabezpieczony przed odczytem lub nie może być odczytany przez serwer.
      Jeśli myślisz, że jest to błąd tego serwera, skontaktuj się z administratorem.

      Error 403
      localhost
      Apache/2.4.3 (Win32) OpenSSL/1.0.1c PHP/5.4.7"
      

      Próbowałem w htacces wyciać application z:

      "RewriteRule ^(?:application|modules|system)\b.* index.php/$0 [L]" 
      

      oraz wszelkie kombinacje na nich. NIC

      Wiem że pewnie to jakaś głupota straszna, ale po prostu nie mam już pomysłu co jest nie tak. Prosze o pomoc.

      • Mariusz napisał:

        Czy w bootstrapie też podałeś katalog?
        Czy masz moduł rewrite apache? Jeśli tak, to czy masz dla katalogu opcję:

        AllowOverride All
        

        i w .htaccess

        Options FollowSymLinks
        
        • Bartek napisał:

          Tak, w obu przypadkach.

          • Mariusz napisał:

            A po zmianach restartowałeś apache?
            Spróbuj ew. w .htaccess zamienić:

            RewriteRule .* index.php/$0 [PT]
            

            na

            RewriteRule .* index.php?/$0 [PT,L,QSA]
            
          • Bartek napisał:
            Kohana::init(array(
                'base_url'   => 'http://localhost/005PHPs/KAHANAs/kohana-3.3.0/Koh_08_Complete/',
                'index_file' => FALSE,
            

            oraz

                AllowOverride All
                Require all denied
            
          • Bartek napisał:

            Oczywiście restartowałem apacha po zmianach. zmiana na

            RewriteRule .* index.php?/$0 [PT,L,QSA]
            

            nic nie dała.

          • Mariusz napisał:

            OK a w .htaccess masz?

            RewriteBase /005PHPs/KAHANAs/kohana-3.3.0/Koh_08_Complete/
            

            Możesz też zrobić sobie wirtualny serwer, tak aby po wpisaniu w url, np. kohana/ otwierało aplikację z tej lokalizacji.

          • Bartek napisał:

            HA!!!
            Miałem:

            RewriteBase /http://localhost/005PHPs/KAHANAs/kohana-3.3.0/Koh_08_Complete/

            Teraz wyskoczył błąd bazy:
            syntax error, unexpected ‚password’ (T_STRING), expecting ‚)’
            ‚hostname’ => ‚localhost’,

            nie nadałem hazłe w bazie, a pewnie jest w sql nadany..

          • Mariusz napisał:

            Pokaż jak wygląda config database (application/config/database.php), sekcja default (wklej w tag php albo code żeby wp ni zgubił apostrofów).

          • Bartek napisał:
            'hostname'   => 'localhost',
            			'database'   => 'kohanaorm',
            			'username'   => 'root,
            			'password'   => '',
            			'persistent' => FALSE,
          • Mariusz napisał:

            Rozumiem, że hasło ukryłeś, brakuje apostrofu po nazwie użytkownika.

          • Bartek napisał:

            apostrof po root… juz działa..

            Super, jesteś Wielki (sorr, jestem hetero!!!) ;-) )

          • Bartek napisał:

            Jeszcze raz witam,

            2 pytanka:

            1) jak jest dodawane 1 do logins w bazie user po każdym logowaniu?

            2) jak powinna wyglądać funkcja do wysyłania maila?

          • Mariusz napisał:

            +1 dodaje akcja complete_login modelu user z orm auth (/modules/orm/classes/model/auth/user.php)

            public function complete_login()
            	{
            		if ($this->_loaded)
            		{
            			// Update the number of logins
            			$this->logins = new Database_Expression('logins + 1');
            
            			// Set the last login date
            			$this->last_login = time();
            
            			// Save the user
            			$this->update();
            		}
            	}
            

            Co do wysyłania maili, to możesz użyć swiftmailer

          • Bartek napisał:

            Dzięki wielkie

          • Bartek napisał:

            Czyli konstruując apkę musisz albo:
            1) dopasować bazę do ORM,
            2) zmodyfikować wpisy w ORM dopasowując je do swojej bazy..

            Dobrze to rozumiem?

          • Mariusz napisał:

            Tak, trzeba dostosować. Ja to robię tak, że do logowania wykorzystuję ORM Auth i jej tabele z pliku (modules/orm/auth-schema-mysql.sql). Jeśli potrzebuję jakichś dodatkowych kolumn, to po prostu je dodaję do tabeli users. Inne tabele z aplikacji np. articles mogą mieć relacje z users id, wtedy wystarczy w modelu określić typ:

            class Model_Article extends ORM {
            
                protected $_belongs_to = array(
                    'user' => array(),
                );
            }
            

            A w users:

            class Model_User extends Model_Auth_User{
                protected $_has_many = array(
                    'article' => array(),
                );
            

            Powiąże to artykuły użytkownika po kolumnie user_id w tabeli articles. Wtedy, aby pobrać artykuły tego użytkownika możemy użyć:

            $id = 1;
            $user = ORM::factory('user', $id);
            $articles = $user->article->find_all();
            

            Trzeba jednak pamiętać o defaultowych relacjach z pliku modules/orm/classes/model/auth/user.php

            protected $_has_many = array(
            		'user_tokens' => array('model' => 'user_token'),
            		'roles'       => array('model' => 'role', 'through' => 'roles_users'),
            	);
            

            Gdyż powinny być powielone w modelu w applications, jeśli podajemy inne swoje.

          • Bartek napisał:

            Mój „problem” polega na tym że mam już dość rozbudowaną bazę, której nie chciałbym zmieniać dramatycznie (dodanie paru kolumn to nie problem, ale zmodyfikowanie całej bazy do tego modelu – o to już problem.
            Czy aby móc korzystać z dobrodziejsw orm i kohana mogę jakoś dopasować do mojej bazy… w jakich to będzie plikach – czy modyfikować modules/orm… czy raczej dobudować model w app/ rozszerzając model ORM?

          • Mariusz napisał:

            Aby używać orm, wystarczy stworzyć plik modelu. Zalecane jest jednak używanie anglojęzycznych nazw w liczbie pojedynczej dla nazw modeli i liczbie mnogiej dla nazw tabel, np.
            user > users,
            articel > articles,
            category > categories
            bo framework zamienia nazwę modelu na l. mnogą jeśli wyraz jest nieregularny to nazwa modelu i tabeli może być taka sama.

            Jeśli chcemy tylko używać ORM, aby łatwiej i szybciej tworzyć zapytania, to wystarczy plik (dla tabeli posts):

            class Model_Post extends ORM {}
            

            I już można w kontrolerach używać łatwego języka orm:

            $posts = ORM::factory('post')->where('state', '=', 1)->find_all()
            

            Można też zadeklarować własne powiązania kolumn, jeśli tabele mają relacje i będziemy ich używać w orm.
            Jeśli chcesz korzystać z logowania Auth, wystarczyłoby, abyś dostosował tabelę users, i dodał tą dla ról i tokenów, a reszta może być bez zmian. Jeśli masz własne logowanie i role to nic nie musisz robić. Wystarczy stworzyć prosty model dla danej tabeli.

          • Bartek napisał:

            czy mógłbyś pokazać przykład takiego modelu? Próbuję już od 3 dni i d…pa.
            Tabela Login (zamiast user): ID_Login(zamiast username, password, pesel(FK), logins, last_logins. nie ma emaila.

          • Mariusz napisał:

            Musiałbyś dostosować też moduł auth orm. Może łatwiej byłoby tabelę z użytkownikami przerobić?

Dodaj komentarz

Dodając kod PHP używaj tagów: [php][/php]

*