webForumDet fria alternativet

MVC i php, mata in data från databas dirket på startisdan.

PHP

19 svar · 1 601 visningar · startad av Mycodez

Medlem sedan feb. 2010445 inlägg
Frågan#1

Hej,
Som rubriken lyder sitter jag och har pillat på en MVC. Är ganska ny inom detta med OOP programmering, så ni vet.

jag har du en MVC, som har uppbyggnaden med mappar och filer direkt på rooten som ser ut så här:
Config (datasbaskopplingar och databasanslutning)
controllers (styr varje enskild sida)
libs (Mycket bas grejer, alla Bas kontrollers finns här, View, database, bootstrap, session, model, controller)
Model (när sidan du e på jobbar med databasen då kopplas kontrollen till modelen.)
public (styles och sånt)
views ( gör så något visas(här har sen varje sida sin mapp t.ex. index har en med fil index, och about har en som heter about med filen index.)

sen lösa filer har jag en index.php fil som (ska göra så index.php vissar här är problemet), och en fin .htaccess

men problemet är nu att jag vill i index alltså startsidan, som man kan se, så ska jag hämta data till den sidan från en databas (det som ska visas alltså) men detta hämtas från en mobel som heter index_model.php i mappen models.

men det är Bootstrap, som visar/styr det hela kan man säga.
Bootstrap.php ser ut såhär (ligger i mappen libs)

<?php

class Bootstrap {

	function __construct() {

		$url = isset($_GET['url']) ? $_GET['url'] : null;
		$url = rtrim($url, '/');
		$url = explode('/', $url);

		//print_r($url);
		
		if (empty($url[0])) {
			require 'controllers/index.php';
			$controller = new Index();
			$controller->index();
			return false;
		}

		$file = 'controllers/' . $url[0] . '.php';
		if (file_exists($file)) {
			require $file;
		} else {
			$this->error();
		}
		
		$controller = new $url[0];
		$controller->loadModel($url[0]);

		// calling methods
		if (isset($url[2])) {
			if (method_exists($controller, $url[1])) {
				$controller->{$url[1]}($url[2]);
			} else {
				$this->error();
			}
		} else {
			if (isset($url[1])) {
				if (method_exists($controller, $url[1])) {
					$controller->{$url[1]}();
				} else {
					$this->error();
				}
			} else {
				$controller->index();
			}
		}
		
		
	}
	
	function error() {
		require 'controllers/error.php';
		$controller = new Error();
		$controller->index();
		return false;
	}

}

och index.pph (den som ligger ute ser ut såhära (

<?php

require 'config/paths.php';
require 'config/database.php';
require 'config/constants.php';

// Also spl_autoload_register (Take a look at it if you like)
function __autoload($class) {
	require LIBS . $class .".php";
}

$app = new Bootstrap();

index_model.php (ligger i mappen models)

class Index_Model extends Model {

	public function __construct() {
		parent::__construct();
	}
	
	public function pageInfo(){
		$sth = $this->db->prepare('SELECT * FROM pages WHERE page="index"');
		$sth->execute();
		return $sth->fetchAll();
	}

}

Men hur ska jag nu gå till väga att få in modelen. så den e med i början, detta funkar bara när man skriver in (där de ligger nu) mvc.mycodez.se om detta ska funka nu med modelen så måste man skriva in mvc.mycodez.se/index

Hoppas ni förstår hur jag vill ha det :)
Mycket glad för snabba svar

Medlem sedan juli 2003555 inlägg
#2

Hm, nu kan jag inte php särskilt bra, och jag har inget svar på din fråga rakt av, men jag ser ett uppenbart brott mot MVC: din model är konstig. Controllern ska fylla din model med data, just nu är din model helt självgående med databaskoppling och sql-fråga - du tappar lite av poängen med MVC då.

Medlem sedan feb. 2010445 inlägg
#3

Vadå så en mvc ska inte ha någon databas alltså alltså?

Medlem sedan dec. 2004736 inlägg
#4

Jo, det ska (kan, du behöver inte nödvändigtvis ha en) du ha i MVC också.

Men modellen ska vara en modell enbart. Alltså, innehålla ett antal variabler, som då gärna kan motsvara en entitet i databasen. Men inte mer.

Din modell ska alltså egentligen inte innehålla några funktioner alls, sånär som på en konstruktor om det behövs.

Men modell ska, som onkelborg säger, inte fylla sig själva med data. Det gör controllern.

Tänk:
Controllerna hämtar den data som behövs och skapar en modell med den datan. Sedan kan den modellen skickas till vyn.

Medlem sedan feb. 2010445 inlägg
#5

Så jag har alltså tänkt helt fel?

Medlem sedan maj 20012 237 inlägg
#6

Nu tycker jag att det är lite villt här!

1) "Controllern ska fylla din model med data"

Detta låter väldigt fel. En kontroller bör inte fylla en model med data. En kontroller skall inte göra mycket mer än delegera hämtning av data från en model (inte nödvändigtvis från en databas!) eller en service och sedan ge view-lagret datan.

Exempel:

function listUsersAction()
{
    $users = $this->service->getUsers();
    
    $this->view->users = $users;
}

På detta viset kan vi hålla kontrollern tom och ren från business-logik och annat tjafs (googla efter "Fat models, thin controllers").

Dessutom får du bättre möjligheter för återanvändning av kod. Det blir också lättare att "Unit testa" koden då koden inte är bunden till en kontroller. En kontroller är en rätt så tung sak att skapa i många fall då den är beroende av hela MVC-stacket som måste bootstrappas varje gång.

2) "Men modellen ska vara en modell enbart. Alltså, innehålla ett antal variabler, som då gärna kan motsvara en entitet i databasen. Men inte mer."

En model behöver inte motsvara en rad i en databas, även om många tutorials på nätet får det att se ut så.

Tänk om applikationen inte lagrar datan i en databas utan använder sig utav modeller ändå. Datan kan ju komma från en web service. Ny data kanske även skickas med en web service –anrop.

En model får visst ha funktioner (metoder). En model kan t ex beskriva en användares funktioner och egenskaper. Dessutom kan den innehåller funktioner, vars uppgift är att validera datan som ancändar-objektet tar emot.

Visst, informationen i en model kan lagras i databasen men i så fall kan man och bör man använda sig av en sk data mapper som mappar en model med databas-lagret. Eller så har man en service-lager som hanterar lagringen av datan. T ex:


// Exempel 1: datan lagras i en databas
class UserService
{
    public function saveUser(User $user)
    {
        if(!$this->isValid($user) )
        {
            return false;
        }
        
        $this->mysqlDb->persist($user);
        
        return $user; 
    }
}

// Exempel 2: datan lagras i Amazon S3
class UserService
{
    public function saveUser(User $user)
    {
        if(!$this->isValid($user) )
        {
            return false;
        }
        
        $this->amazonS3->save($user);
        
        return $user; 
    }
}

Denna abstraktion tillåter dig att byta ut lagret som lagrar datan (antingen en MySql-databas eller Amazon S3).

Medlem sedan feb. 2010445 inlägg
#7

Okej, så jag ha kan man säga fel stycktur i mitt tänk? Men i den koden jag har nu, i bootstrap går det att anropa metoden, som jag har i model namnet index_models.php på något sätt som jag byggt det nu?

Medlem sedan juli 2003555 inlägg
#8

jme: Du är lite rolig du :P Först säger du att jag har fel, sedan visar du ett kodexempel som gör exakt vad jag sa ;)

Medlem sedan maj 20012 237 inlägg
#9

Var gör jag samma sak?

Medlem sedan juli 2003555 inlägg
#10

>>1) "Controllern ska fylla din model med data"
>
>Detta låter väldigt fel. En kontroller bör inte fylla en model med data

vs

> $this->view->users = $users;

:)

btw, view borde vara model

Medlem sedan maj 20012 237 inlägg
#11

I denna tråd ser jag "model" som en enstaka objekt men och det kan vara en lite felaktig tolkning. Man pratar ofta om "Domän-modeller" som ett lager istället, inte som enstaka objekt, och som innehåller affärslogiken.

Jag har själv ofta tolkat begreppet "model" just som en enstaka klass. Fast nuförtiden så har jag börjat att köra med begrepp som service och entiteter och här så skulle service- och entitet-klasserna höra till domän-lagret, eller domän-modellen.

Se:

http://en.wikipedia.org/wiki/Domain_model
http://martinfowler.com/eaaCatalog/domainModel.html

MEN:

Jag tycker inte att jag fyller modellen med data i kontrollern i sig(!) Datan kommer från service-objektet. Vad som händer där är att kontrollern ber service-klassen om att få ett gäng User-objekt som kontrollern sedan ger till vy-objektet.

Onkelborg: kanske kan du utveckla hur du tolkar termen"model"?

Medlem sedan juli 2003555 inlägg
#12

Kontrollerna fyller modellen med data, sen att kontrollern hämtar den datan någonstans ifrån är en helt annan femma. För MVC-principen spelar det ingen roll hur kontrollern gör för att få fram data att fylla modellen med. Sen att det kan vara en bra idé att inte placera all logik i kontrollern håller jag med om, men det har knappast att göra med själva MVC :)

Medlem sedan maj 20012 237 inlägg
#13

Kan du visa med kod vad du menar med "Kontrollerna fyller modellen med data"?

Medlem sedan juli 2003555 inlägg
#14

Pseudo:

Kontrollern:

var modell = new ModellAvNågonSort();
modell.Egenskap = SkaffaNågonSortsDataIfrånNågonstansPåNågotSätt();
return this.View(modell);

Medlem sedan maj 20012 237 inlägg
#15

Vad är denna Egenskap?

Tänk om vi har modellen 'User' (förenklad!):

class User
{
    public $firstname;
    public $lastname;
    public $email;
}

Skulle den då användas (enligt ditt exempel) på följande vis?


$user = new User();
$user->firstname = $xxx->getFirstname();
$user->lastname = $xxx->getLastname();
$user->email = $xxx->getEmail();

$this->view($user);

Varför kan inte kontrollern få modelen skapad och klar för användning?

$this->view($user) = $xxx->getUser();

Medlem sedan juli 2003555 inlägg
#16

Jag förstår faktiskt inte problemet. Den där egenskapen skulle kunna vara både en hel användare, eller bara förnamnet. Vad spelar det för roll? I ditt exempel är User modellen, kontrollern ber funktionen getUser att fylla den, och skickar den sedan till vyn. Kontrollern ser till att det blir gjort.

Om jag ska hårdra det du säger om att kontrollern inte ska fylla modellen (vilket står i total motsats till dina kodexempel) så skulle din psedukod se ut ungefär såhär:

$this->view();

Var modellen sen ska komma ifrån vete sjutton..

Medlem sedan maj 20012 237 inlägg
#17

I mina exempel fyller inte kontrollern modellen utan kontrollern får den av en service. Service-objektet skapar och fyller model-objektet. Kontrollern ber servicen om att få denna modell som sedan ges till vyn.

Observera att kontroller skapar inte modellen SJÄLV, utan ber om den(!). Det finns ingen new User() i mina exempel i kontollern.

Hence:

$this->view->user = $service->getUser();

Observera också att vy-lagret inte har en egen view model.

Medlem sedan feb. 2010445 inlägg
#18

nu ska jag vissa er hela min kod som gör att sidan visas med denna url: laggt demo på denns sidan nu med, har haft problem med mitt internet, drf mitt dåliga svar här, cp data -.- http://mvc.mycodez.se/ där är url, som jag vill att saker ska ske, utan att man ska behöva skriva http://mvc.mycodez.se/index okej,

men först index, ligger direkt i rooten

<?php

require 'config/paths.php';
require 'config/database.php';
require 'config/constants.php';

// Also spl_autoload_register (Take a look at it if you like)
function __autoload($class) {
	require LIBS . $class .".php";
}

$app = new Bootstrap();

Bootstrap.php ligger i en map kallad libs.

<?php

class Bootstrap {

	function __construct() {

		$url = isset($_GET['url']) ? $_GET['url'] : null;
		$url = rtrim($url, '/');
		$url = explode('/', $url);

		//print_r($url);
		
		if (empty($url[0])) {
			require 'controllers/index.php';
			$controller = new Index();
			$controller->index();
			return false;
		}

		$file = 'controllers/' . $url[0] . '.php';
		if (file_exists($file)) {
			require $file;
		} else {
			$this->error();
		}
		
		$controller = new $url[0];
		$controller->loadModel($url[0]);

		// calling methods
		if (isset($url[2])) {
			if (method_exists($controller, $url[1])) {
				$controller->{$url[1]}($url[2]);
			} else {
				$this->error();
			}
		} else {
			if (isset($url[1])) {
				if (method_exists($controller, $url[1])) {
					$controller->{$url[1]}();
				} else {
					$this->error();
				}
			} else {
				$controller->index();
			}
		}
		
		
	}
	
	function error() {
		require 'controllers/error.php';
		$controller = new Error();
		$controller->index();
		return false;
	}

}

sen nu när inget finns att hämta i urlen så skrivs det ut index sidan som görs av controllern, som ligger i en denna väg root -> controllers -> index.php

class Index extends Controller {

	function __construct() {
		parent::__construct();
	}
	
	function index() {
		$this->view->pageInfo = $this->model->pageInfo();
		$this->view->render('index/index');
	}
	
}

view filen för index ligger på denna väg root-> view -> index-> index.php

<?php
	foreach($this->pageInfo as $key => $value) {
		$title = $value['pageTitle'];
		$body = $value['pageContent'];
	}
?>
<h1><?php echo $title; ?></h1>
<?php echo nl2br($body); ?>

nu så ser ni att jag har en controller och en view fil och dem hämndes in via index.php filen som ligger på rooten, och dem ligger i mappen libs, Controller.php ser ut

<?php

class Controller {

	function __construct() {
		//echo 'Main controller<br />';
		$this->view = new View();
	}
	
	public function loadModel($name) {
		
		$path = 'models/'.$name.'_model.php';
		
		if (file_exists($path)) {
			require 'models/'.$name.'_model.php';
			
			$modelName = $name . '_Model';
			$this->model = new $modelName();
		}		
	}

}

och view.php

<?php

class View {

	function __construct() {
		//echo 'this is the view';
	}

	public function render($name, $noInclude = false)
	{
		if ($noInclude == true) {
			require 'views/' . $name . '.php';	
		}
		else {
			require 'views/header.php';
			require 'views/' . $name . '.php';
			require 'views/footer.php';	
		}
	}

}

sen har vi även models.php
i libs

<?php

class Model {

	function __construct() {
		$this->db = new Database(DB_TYPE, DB_HOST, DB_NAME, DB_USER, DB_PASS);
	}

}

och alla "modeller" ligger i en egen map på rooten heter Models
där har vi index sidans model som heter index_model.php som ser ut

<?php

class Index_Model extends Model {

	public function __construct() {
		parent::__construct();
	}
	
	public function pageInfo(){
		$sth = $this->db->prepare('SELECT * FROM pages WHERE page="index"');
		$sth->execute();
		return $sth->fetchAll();
	}

}

okej hoppas ni förstår,

Medlem sedan maj 20012 237 inlägg
#19

En sak som jag fastnade på är:

foreach($this->pageInfo as $key => $value) {
        $title = $value['pageTitle'];
        $body = $value['pageContent'];
    }

värdet i $title och $body ersätts varje iteration.

Medlem sedan feb. 2010445 inlägg
#20

Okej, men alla värden jag får som return värde kommer i en array, och jag testa detta och detta funkar, jag gör samma princip på alla olika sidor, jag har på siten.

Liksom vet inte riktigt hur jag ska förklara det hela :/

265 ms totalt · 4 externa anrop · v20260731065814-full.868a69e5
124 ms — deklarationer (db)
0 ms — hämta statistik (cache)
137 ms — hämta tråd, inlägg och bilagor (db)
124 ms — ändringar (db)