Blog tutorial – część 4

W poprzednich częściach stworzyliśmy prostą makietę dla naszej aplikacji, projekt bazy danych oraz napisaliśmy pliki z migracjami dla bazy danych. W tej części zajmiemy się logowaniem do aplikacji. Na początek stworzymy model oraz kontroler. Zanim jednak w ogóle poruszymy ten temat, warto abyśmy zapoznali się z materiałem z podręcznika na ten temat: modele, klasa Active Record i kontrolery.

Teraz można założyć, że mamy już podstawy, więc możemy przejść do konkretów. Nasz model będzie nosił nazwę User_model, a kontroler Users – w plikach znajdzie się kod odpowiedzialny za uwierzytelnianie użytkownika w naszej aplikacji. Na początek utwórzmy plik w katalogu application/models o nazwie user_model.php:

<?php defined('BASEPATH') OR exit('No direct script access allowed');

class User_model extends CI_Model
{
	// Nazwa tabeli, z której będziemy korzystać w modelu
	public $table = 'users';
	
	/**
	 * Logowanie użytkownika 
	 * Sprawdza, czy użytkownik o podanym adresie email i haśle istnieje w bazie danych
	 *
	 * @access	public
	 * @return	mixed
	 */
	public function login($email, $password)
	{
		// Jeśli w bazie zostanie znaleziony użytkownik o podanym adresie email i haśle, 
		// to wynik zostanie zwrócony do kontrolera w postaci tablicy, w innym wypadku otrzymamy pusty wynik.
		return $this->db->where(array('email' => $email, 'password' => $password))->get($this->table)->row_array();
	}
}

/* End of file user_model.php */
/* Location: ./application/models/user_model.php */

Jeśli zastanawiasz się co w powyższym kodzie robi funkcja row_array(), to warto zapoznać się ze sposobami na generowanie wyników zapytań w CodeIgniterze – wtedy wszystko powinno być jasne. Teraz możemy się zająć kontrolerem. Utwórzmy plik users.php w katalogu application/controllers o takiej treści:

<?php defined('BASEPATH') OR exit('No direct script access allowed');

class Users extends CI_Controller
{

	public function login()
	{
		// Ładujemy bibliotekę walidacji formularza
		$this->load->library('form_validation');
		// Określamy jakie tagi będą otaczać komunikat błędu walidacji. 
		// To kwestia stricte kosmetyczna - dostosowanie wyglądu do Twitter Bootstrap.
		$this->form_validation->set_error_delimiters('<div class="alert alert-error"><a class="close" data-dismiss="alert" href="#">×</a>', '</div>');

		// Ustalamy reguły walidacji dla formularza
		$this->form_validation->set_rules('email', 'Login', 'required|trim|valid_email|xss_clear');
		$this->form_validation->set_rules('password', 'Hasło', 'required|trim|sha1');

		// Sprawdzamy, czy formularz został wysłany i czy wystąpiły błędy walidacji.
		if ($this->form_validation->run() === FALSE)
		{
			// Ładujemy helper Form, aby ułatwić sobie pracę z formularzem.
			$this->load->helper('form');

			// Jeśli walidacja formularza nie powiodła się (lub metoda jest wywołana po raz pierwszy), wyświetlamy pliki z katalogu partials oraz widok "user_login",
			// który znajduje się w katalogu "application/views". Ewentualne informacje o błędach w formularzu są przekazywane automatycznie.
			$this->load->view('partials/header');
			$this->load->view('user_login');
			$this->load->view('partials/footer');
		}
		else
		{
			// Jeśli walidacja formularza powiodła się, przypisujemy odpowiednie zmienne z tablicy $_POST do zmiennej $email i $password
			$email = $this->input->post('email');
			$password = $this->input->post('password');

			// Ładujemy wcześniej utworzony model
			$this->load->model('user_model');

			// Wywołujemy metodę "login" modelu "user_model" wraz z parametrami i przypisujemy zwrócony wynik do zmiennej $user
			if ($user = $this->user_model->login($email, $password))
			{
				// Jeśli zwrócony wynik nie jest pusty (czyli znaleziono użytkownika o podanym adresie email i haśle),
				// ustawiamy zmienną sesyjną o nazwie "user_id" na wartość unikalnego identyfikatora użytkownika.
				$this->session->set_userdata('user_id', $user['id']);
				// Ustawiamy zmienną flashadata o nazwie success i przypisujemy do niej komunikat o powodzeniu logowania.
				$this->session->set_flashdata('success', 'Logowanie przebiegło pomyślnie!');
				// Przekierowujemy użytkownika na stronę ze wszystkimi postami podając w funkcji nazwę kontrolera/metody, która ma zostać wywołana.
				// W tym wypadku nie ma znaczenia, czy podamy 'posts/index', czy samo 'posts', ponieważ CodeIgniter domyślnie zinterpretuje samo 'posts' jako wywołanie
				// metody 'index' - jako domyślnej funkcji dla każdego kontrolera.
				redirect('posts');
			}
			else
			{
				// Jeśli zwrócony wynik jest pusty (czyli nie znaleziono użytkownika o podanym adresie email i haśle),
				// ustawiamy zmienną flashadata o nazwie error i podajemy komunikat o niepowodzeniu logowania.
				$this->session->set_flashdata('error', 'Podany login lub hasło są nieprawidłowe!');
				// Przekierowujemy użytkownika na stronę logowania podając dla funkcji nazwę kontrolera/metody, która ma zostać wywołana
				redirect('users/login');
			}
		}
	}

	public function logout()
	{
		// Niszczymy aktualną sesję
		$this->session->sess_destroy();
		// Przekierowujemy na stronę logowania
		redirect('users/login');
	}
}

/* End of file users.php */
/* Location: ./application/controllers/users.php */

Teraz pora przejść do widoków. Oczywiście nie będziemy omawiać całej struktury Twitter Bootstrap – jeśli chcesz ją poznać, musisz to zrobić samodzielnie. Na początek utworzymy dwa „partiale” – w katalogu application/views, tworzymy katalog partials, a w nim dwa pliki z nagłówkiem szablonu i stopką. Te dwa pliki będziemy wczytywali za każdym razem, co widać w metodzie login.
Kod zawarty w samych partialach jest stosunkowo prosty i wygląda następująco, header.php:

<!DOCTYPE html>
<html lang="pl">
  <head>
    <meta charset="utf-8">
    <base href="<?php echo base_url(); ?>">
    <title>Blog tutorial</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="author" content="CodeIgniter.org.pl">

    <!-- Le styles -->
    <link href="assets/css/bootstrap.css" rel="stylesheet">
    <style type="text/css">
      body {
        padding-top: 60px;
        padding-bottom: 40px;
      }
    </style>
    <link href="assets/css/bootstrap-responsive.css" rel="stylesheet">

    <!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
    <!--[if lt IE 9]>
      <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
    <![endif]-->

  </head>

  <body>

    <div class="navbar navbar-inverse navbar-fixed-top">
      <div class="navbar-inner">
        <div class="container">
          <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </a>
          <a class="brand">Blog tutorial</a>
          <div class="nav-collapse collapse">
            <ul class="nav">
              <!-- 
                  Poniżej sprawdzamy, czy dana pozycja w menu jest aktualnie odwiedzaną stroną. 
                  Jeśli tak, to do tagu <li> dodajemy klasę "active", która zmieni wygląd tego elementu.
                  Aby dowiedzieć się jak działa funkcja rsegment zajrzyj do podręcznika: http://podrecznik.codeigniter.org.pl/libraries/uri.html
              -->
              <li <?php echo ($this->uri->rsegment('2') == 'index') ? 'class="active"' : ''; ?>><a href="<?php echo site_url('posts/index'); ?>">Lista</a></li>
              <li <?php echo ($this->uri->rsegment('2') == 'add') ? 'class="active"' : ''; ?>><a href="<?php echo site_url('posts/add'); ?>">Dodaj</a></li>
            </ul>
            <ul class="nav pull-right">
              <!-- 
                  Sprawdzamy, czy użytkownik jest zalogowany i w zależności od tego wyświetlamy
                  odpowiednie pozycje w menu. 
              -->
              <?php if ($this->session->userdata('user_id')): ?>
                <li><a href="<?php echo site_url('users/logout'); ?>">Wyloguj</a></li>
              <?php else: ?>
                <li <?php echo ($this->uri->rsegment('2') == 'login') ? 'class="active"' : ''; ?>><a href="<?php echo site_url('users/login'); ?>">Logowanie</a></li>
              <?php endif; ?>
            </ul>
          </div><!--/.nav-collapse -->
        </div>
      </div>
    </div>

    <div class="container">
      <!-- Jeśli istnieją komunikaty o błędach walidacji, wyświetl je. -->
      <?php if (validation_errors()): ?>      
          <?php echo validation_errors(); ?>
      <?php endif; ?>

      <!-- Jeśli istnieje zmienna flashdata o nazwie 'error' wyświetl ją. -->
      <?php if ($this->session->flashdata('error')): ?>
        <div class="alert alert-error">
          <a class="close" data-dismiss="alert" href="#">×</a>
          <?php echo $this->session->flashdata('error'); ?>
        </div>
      <?php endif; ?>

      <!-- Jeśli istnieje zmienna flashdata o nazwie 'success' wyświetl ją. -->
      <?php if ($this->session->flashdata('success')): ?>
        <div class="alert alert-success">
          <a class="close" data-dismiss="alert" href="#">×</a>
          <?php echo $this->session->flashdata('success'); ?>
        </div>
      <?php endif; ?>

Zauważ, że w tym pliku korzystamy m.in. z funkcji base_url i site_url z helpera Url oraz klasy sesji. Plik header.php będzie wczytywany w obrębie całej naszej aplikacji – mamy więc dwa wyjścia: albo będziemy ładować helper Url i klasę sesji w każdym kontrolerze, albo skorzystamy z automatycznego ładowania. W tym wypadku wybierzemy to drugie rozwiązanie, ponieważ jest wygodniejsze. Wystarczy więc, że zmienimy odpowiednie linijki kodu w pliku application/config/autoload.php na następujące: $autoload[’helper’] = array(’url’); oraz $autoload[’libraries’] = array(’database’, 'session’); .

Kod partialu footer.php:

      <hr>

      <footer>
        <p>&copy; 2012 CodeIgniter.org.pl</p>
      </footer>

    </div> <!-- /container -->

    <!-- Le javascript
    ================================================== -->
    <!-- Placed at the end of the document so the pages load faster -->
    <script src="assets/js/jquery-1.8.2.min.js"></script>
    <script src="assets/js/bootstrap.min.js"></script>

  </body>
</html>

Teraz czas na szablon, który wykorzystamy do logowania, czyli plik user_login.php.

	<div class="row">
		<div class="span4 offset4 well">
			<legend>Logowanie</legend>     
			<!-- Otwieramy formularz za pomocą funkcji z helpera Form. -->    	
			<?php echo form_open(); ?>
				<!-- 
					Również do definicji pól formularza możemy użyć funkcji z helpera Form, 
					ale w tym przypadku nie widać specjalnych korzyści dla których musielibyśmy to robić, 
					dlatego zostaniemy przy "normalnym" zapisie.
				-->
				<input type="email" id="email" class="span4" name="email" placeholder="Email">
				<input type="password" id="password" class="span4" name="password" placeholder="Hasło">
				<button type="submit" name="submit" class="btn btn-info btn-block">Zaloguj</button>
			<!-- Zamykamu formularz. -->
			<?php echo form_close(); ?>
		</div>
	</div>

To by było na tyle. Teraz wystarczy, że przejdziemy na stronę logowania pod adres: http://localhost/blogtutorial/index.php/users/login i zalogujemy się podając dane z poprzedniej części tutorialu (login: admin@admin.dev, hasło: admin). Oczywiście po zalogowaniu CodeIgniter będzie próbował odnaleźć kontroler „Posts” (tak jak sobie tego życzyliśmy), a z racji tego, że jeszcze takowego nie utworzyliśmy, otrzymamy komunikat „404 Page Not Found”. Wystarczy jednak jeszcze raz przejść na stronę logowania aby zobaczyć komunikat o poprawnym zalogowaniu, a w prawym rogu menu zamiast opcji „Logowanie” zobaczymy napis „Wyloguj”. To znak, że logowanie przebiegło pomyślnie.

Kod źródłowy tutorialu jak zawsze jest dostępny na githubie. W następnej części zajmiemy się dodawaniem postów do naszego bloga.

29 komentarzy do “Blog tutorial – część 4”

  1. Przydałaby się informacja, że należy utworzyć foldery:
    /assets/css,
    /assets/js,
    a w nich, umieścić odpowiednie pliki bootstrap i jquery.

    Odpowiedz
    • Teraz na pewno nikt nie przeoczy tej informacji. Ale mam nadzieję, że każdy kto zapozna się z kodem, będzie tego świadomy. Dodatkowo do dyspozycji są zawsze źródła na github.

      Odpowiedz
  2. Na marginesie, skąd wzięła się fotka obok mojego imienia?
    Ja jej nie dodawałem, co więcej, nawet się nie rozpoznaję.

    Odpowiedz
  3. Noom okej, przekopiowałem sobie wszystkie kody na serwer lokalny, odpalam i uzyskuję błąd „Aby użyć klasy sesji są zobowiązane do ustawienia klucza szyfrowania w pliku konfiguracyjnym”. Ustawiam klucz na byle jaki ($config[’encryption_key’]) a tu błąd, że nie ma tabeli „ci_sessions”.

    Dodam, że to mój pierwszy framework, ciężko się do nich przekonałem i podczas pierwszej próby już jakieś przeszkody…

    Odpowiedz
  4. parametry bazy mam dobrze ustawione w database.php a on mi mowi ze nie.
    active records mam na true
    linia 346 w system/core/loader.php wyglada tak: $CI->db =& DB($params, $active_record); czyli zaczytuje parametry bazy i czy ustawiony jest active record. ale nie wiem czemu sie wysypuje takim bledem?

    Unable to connect to your database server using the provided settings.

    Filename: core/Loader.php

    Line Number: 346

    Odpowiedz
    • Witaj lukass. Przyczyn może być wiele i leżą zapewne po stronie konfiguracji Twojego serwera. Na początek możesz sprawdzić, czy poskutkuje ustawienie zmiennej $db['default']['pconnect'] = FALSE;. Później proponuję sprawdzić, czy na pewno możesz się połączyć przy podanych danych dostępowych do mysql poza CI, np:
      mysql_connect('host','user','pass') or die(mysql_error());
      mysql_select_db('nazwabazydanych') or die(mysql_error());

      Ogólnie, z pytaniami zapraszam na forum – tam więcej osób zauważy Twoje pytanie i pomoże rozwiązać problem.

      Odpowiedz
  5. Do walidacji pola e-mail musimy dodać wartość valid_email, ponieważ nie wszystkie przeglądarki obsługują typy pól email w formularzach HTML.

    Odpowiedz
  6. Cześć, mam problem i siedze juz nad tym sporo czasu. Robiłem wszystko tak jak ty, oprócz migracji ( zrobilem wszystko w phpmyadmin) wpisuje email i haslo i nic sie nie dzieje, tzn. nie wyskakuje error 404 tak jak powinno tylko po prostu ciagle jestem na stronie logowania. Sprawdzanie poprawnosci wpisanych danych chyba dziala bo jak wpisalem w polu email bez malpy to wyswietlilo nieprawidlowy email, ale jak daje poprawne dane to nic sie nie dzieje nie wiem czemu. Sciągnalem twoj kod na githubie i jest to samo. Poza tym czy nie pomyliles sie z tym : http://localhost/blogtutorial/index.php/users/login , przeciez widok sie nazywa user_login.

    Dodam ze w autoload mam database i session oczywiscie

    Odpowiedz
    • Witaj Michal i dziękuję za komentarz.
      Błąd jest po mojej stronie, ponieważ udostępniłem wersję z zakomentowaną funkcją redirect. Zarówno artykuł jak i repozytorium na github, zostały poprawione.

      Jeszcze raz dziękuję za zwrócenie uwagi. Jeśli nadal będziesz miał jakieś problemy, to pytaj.

      Odpowiedz
  7. Faktycznie, ten redirect byl zakomentowany ale i tak to nie ma znaczenia w moim przypadku poniewaz to sie odnosilo do niepoprawnego loginu, tzn jezeli login jest nie poprawny do odeslij mnie do strony logowania, okej – tylko ja mam problem ze loguje poprawne dane i nie przechodze dalej czyli w tym przypadku do 'posts’ – redirect(’posts’) – u mnie wlasnie, tam mnie nie chce poprowadzic.

    Przesylam ci video gdzie pokazuje problem : http://screencast.com/t/40R7T7CvGGDf

    w video jest redirect(users/posts) ale to nie ma znaczenia bo powinien byc blad 404 – nie ma takiej strony prawda ? A nie dzieje sie kompletnie nic.

    Odpowiedz
    • Przepraszam, że tak późno – powiadomienia nam nawaliły :(

      Pewnie już rozwiązałeś problem – w każdym razie chodziło o brak określonego adresu w form_open.

      Odpowiedz
  8. Cześć
    mam pewien problem a mianowicie strona logowania w IE 10 ładuje się bez styli CSS, natomiast po zalogowaniu kolejna strona jest już ze stylami?

    Macie może jakiś pomysł czym to jest spowodowane?

    Odpowiedz
      • Przepraszam, że tak późno – powiadomienia nam nawaliły :(

        Wystarczy zmienić wartość dla 'default_controller’ w pliku 'application/config/routes.php’.

        Odpowiedz
        • jeszcze można sprawdzić czy uzytkownik zalogowany i jak nie to przeniesc go na stronę logowania
          [code]session->userdata(’user_id’)):
          redirect(’strona-logowania’,’refresh’);
          ?>[/code]

          Odpowiedz
  9. Witam! Mam pytanie odnośnie logowania. Co trzeba byłoby zrobić aby mogła się zalogować większa liczba osób np na takiego bloga? Dodałem użytkownika do bazy danych, ale jakoś nie chce mi się zalogować na dane nowego użytkownika. Co zrobić?
    Pozdrawiam, Sebastian

    Odpowiedz
  10. Witam. Mam następujący problem, zrobiłem wszystko tak jak trzeba, ale niestety sesje mi się nie zapisują. I co dziwne inne sesje które stworzyłem działają, tylko te znajdujące się w plikach logowania nie. Bardzo proszę o pomoc.

    Odpowiedz
    • Witaj. Hmm… co masz na myśli pisząc o innych sesjach?

      Możesz dokładnie opisać, co się dzieje i który element nie działa (co się nie zapisuje, a powinno)?

      Odpowiedz
      • Sesje zapisują się w bazie danych, ale nie działają, w sensie nie wyświetla warunków, wyloguj, logowanie przebiegło itd. Pomimo, że w bazie w sesjach są zapisane. Chodzi głównie o to logowanie z poradnika.

        Odpowiedz
  11. Wykonałeś kawał roboty, ale ten „tutorial” praktycznie nic nie wnosi, praktycznie nic tu nie jest omówione, tylko kod do wklepania.

    Odpowiedz
    • Dzięki za opinię.

      Dla mnie osobiście, tutorial powinien przede wszystkim zawierać kod i jego omówienie – to właśnie można tutaj znaleźć (czyli co się dzieje w każdej linijce). Omówienie poszczególnych bibliotek jest dostępne w podręczniku, więc bez sensu tutaj powielać to samo. Wychodzę z założenia, że po przejrzeniu podręcznika niekoniecznie wszyscy muszą wiedzieć jak to wszystko „poskładać do kupy”, więc ten tutorial ma za zadanie własnie to pokazać. Tylko tyle.

      Jeśli masz jakieś konkretne uwagi, czego wg Ciebie tutaj zabrakło – chętnie posłucham.
      Pozdrawiam.

      Odpowiedz

Dodaj komentarz

Ta strona używa Akismet do redukcji spamu. Dowiedz się, w jaki sposób przetwarzane są dane Twoich komentarzy.