multipleselect – sql update

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
simPod
Člen | 383
+
0
-

mam ve formulari multiple select, kazda jeho polozka ma nejakou id hodnotu

kdyz jich vyberu vic, jak ma vypadat db update statement po odeslani formulare? potrebuji do databaze do kolonky vlozit hodnoty 1 a 2 tak, aby sly nasledne pouzit v where IN() (where IN(1,2))

diky

Editoval simPod (8. 5. 2012 17:36)

vvoody
Člen | 910
+
0
-

Absolutne som nepochopil co chces dosiahnut, skus to napisat ludskou recou ;) ak ide len o WHERE IN() tak v dokumentacii je:

$table->where("field", array(1, 2));	//Means field IN (1, 2)
jtousek
Člen | 951
+
0
-

Nette\Database\Selection má metodu update, takže totéž jakobys ty řádky selectoval (druhá tabulka, čtvrtý řádek) a za to připojíš ten ->update(…).

Nauč se hledat. ;-)

EDIT: Hele ty chceš možná něco jinýho… nebude lepší ty záznamy smazat a přidat? Typicky jich bude jiný počet než předtím.

Editoval jtousek (8. 5. 2012 18:30)

simPod
Člen | 383
+
0
-

no, potrebuji do jednoho sloupecku vlozit vice id

a pak nasledne ty id pouzit s ->where()

takze ted resim, jak tam ty id muzu vlozit. Muj zdroj dat je z MultipleSelect prvku z formulare

jtousek
Člen | 951
+
0
-

Jak říkám, zavolej delete na ty staré záznamy (co jsi dával do formuláře jako výchozí hodnoty) a insertni ty nové. API k delete i insert je v dokumentaci, co víc bys chtěl?

vvoody
Člen | 910
+
0
-

simPod: ukaz celu strukturu tej tabulky a opis celu funkcionalitu ktoru riesi, inak sa nepohneme z miesta ;)

simPod
Člen | 383
+
0
-

2jtousek: to neni ono

zkusim to jeste takhle:

CREATE TABLE IF NOT EXISTS `column` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `value_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

ve formulari je multiselect $form->addMultiSelect('select', 'Multiselect', array(1=>'prvni',2=>'druha')

vyberu v multiselectu vsechny polozky a odeslu formular $form->onSuccess[] = callback($this, 'submitFce');

function submitFce($form){
	$values= $form->values;
	$this->context->myModel->updateTable($values->select);
}

Model

...
function updateTable($value){
	$this->conn->table('column')->update(array(
		'value_id' => $value
	))
}
...

a tady to je: promenna $value muze obsahovat 2,3 nebo i 20 idcek, viz hodnota z multiselect

jak je vlozim do DB? A jak je tam vlozim tak, aby pak slo v pohode pouzit ->where(array($value))

je to jasnejsi?

diky

vvoody
Člen | 910
+
0
-

ten formular sa v produkcii bude viacnasobne odosielat a takymto sposobom chces len zaznamenavat pocet kolko krat ktore pole bolo odoslane? Vies, neviem v akom kontexte chces to ->where(array($value)) pouzit.

Kazdopadne pre vkladanie do tabulky pouzi insert ;)

function updateTable($values){
	foreach($values as $value){
		$this->conn
			->table('column')
			->insert(array(
				'value_id' => $value
			));
	}
}

Neviem ako presne na multiple insert, teraz trosku typujem

function updateTable($values){
	$data = array();
	foreach($values as $value){
		$data[] = array('value_id'=>$value);
	}
	$this->conn->table('column')->($data);
}

ale potom by som tu metodu nevolal update ale insert :)

simPod
Člen | 383
+
0
-

tak jeste oprava, jde mi o update:

//$id poslu z prezenteru
function updateTable($id,$value){
        $this->conn->table('column')->where('id',$id)->update(array(
                'value_id' => $value
        ))
}
jtousek
Člen | 951
+
0
-

Ta SQL tabulka je zcela zřetelně špatně navržená pro to co chceš. Mohl bys upřesnit, jaká data se do té tabulky ukládají, co reprezentují ty řádky, co se vybírá v tom multiselectu a tak? Bez toho ti těžko řeknu jak ta tabulka (či tabulky) má vypadat.

EDIT: Znovu – DELETE a INSERT:

function updateTable($id,$values){
        $this->conn->table('column')->where('id',$id)->delete();
	foreach ($values as $value) {
		$this->conn->table('column')->insert(array('id' => $id, 'value_id' => $value));
	}
}

EDIT2: Tohle samozřejmě nemůže fungovat protože id je primární klíč – ta tabulka bude muset být jiná, pravděpodobně se rozloží na dvě nebo primární klíč budou oba dva sloupce.

Editoval jtousek (8. 5. 2012 23:22)

simPod
Člen | 383
+
0
-

tabulka obsahuje nejaka data, ja ji sem zjednodusil. Napr. obsahuje produkty – nazev, cenu a kategorii do ktere produkt spada. Produkt muze spadat do vice kategorii, takze ve sloupci s id kategorie, do ktere spada, potrebuji mit nekdy vice takovych id

jtousek
Člen | 951
+
0
-

Takže jsem to typoval správně. :-) Tohle je klasická relace M:N, která se řeší pomocí 3 tabulek:

  1. tabulka produktů
  2. tabulka kategorií
  3. tabulka vztahů produkt-kategorie, kde máš sloupce např. product_id a category_id + složený primární klíč (PK jsou oba sloupce)

Takže potom jak jsem řekl. Z té vztahové tabulky smažeš všechny výskyty, kde product_id je id produktu se kterým pracuješ. Následně vložíš zcela nové vztahové řádky, pro každou kategorii jeden.

EDIT: Podle tohoto vlákna zatím NDB neumí pracovat s vícesloupcovými PK, takže ta vztahová tabulka bude vypadat asi jako id, product_id, category_id, kde id bude PK, auto increment, ale vlastně se nebude reálně vůbec používat.

Editoval jtousek (9. 5. 2012 11:22)

simPod
Člen | 383
+
0
-

aha, zajimave

jak pak vypada sahnuti si na id kategorie (v modulu)? musim pres nejaky join? jak se to v nette dela?

//neco v tomhle smyslu
function getCategoryId($id){
	$this->conn->table('products')->where('id',$id)->fetch()->category_id
}

dik

jtousek
Člen | 951
+
0
-

V té vztahové tabulce bude product_id nastaven jako foreign key na products.id a obdobně category_id FK na category.id.

Dále pokud vybíráš dle $id tak nemusíš používat where, stačí get (a už pak nevoláš fetch). Navíc typicky nechceš id jedné kategorie, ale chceš iterovat přes všechny (může jich být i 0!).

foreach($this->conn->table('products')->get($productId)->related('product_category') as $product_category) {
	doSomething($product_category->category->id);
}

EDIT: Hlavně si uvědom, že nepracuješ s ID jedné kategorie ale s nějakou množinou – ta kategorie může být jedna, ale taky jich může být deset nebo třeba žádná. Funkce typu getCategoryId tedy nedává žádný smysl, když už tak něco jako getCategories.

EDIT2: Samozřejmě můžeš vyžadovat, že každý produkt spadá do alespoň jedné kategorie – to už ale je na pravidlech jaká si nastavíš pro formulářové prvky a na integritních omezeních v databázi.

Editoval jtousek (9. 5. 2012 12:05)

simPod
Člen | 383
+
0
-

aha. to je pro me neco noveho, foreign keys jsem neznal… kazdopadne se mi tedy povedlo prekopat celou strukturu, ted jeste musim neco udelat s temi sql prikazy…

rekneme, ze jsem volal neco takovehleho:

//prezenter
$this->context->myModel->getProducts()->where('category_id', $id);

/*****/

//model
function getProducts() {
	return $this->connection->table('products')->order('product_name');
}

tam asi budu muset zacti z druhe strany od kategorii ze? zkusil jsem v modelu tohle:

//prezenter
$products = $this->context->productModel->getProductsByCategory($cat);
foreach($products as $product){
	echo $product->products->product_name;
}

//model
function getProductsByCategory($id) {
    return $this->connection->table('categories')->get($id)->related('product_category');
}

ale vraci mi to No reference found for $product_category->products.

jinak spojovaci tabulka vypada takhle:

CREATE TABLE IF NOT EXISTS `product_category` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product_id` int(11) NOT NULL,
  `category_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `category_id` (`category_id`),
  KEY `product_id` (`product_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=4939 ;

--
-- Constraints for dumped tables
--

--
-- Constraints for table `product_category`
--
ALTER TABLE `product_category`
  ADD CONSTRAINT `product_category_ibfk_1` FOREIGN KEY (`product_id`) REFERENCES `products` (`product_id`),
  ADD CONSTRAINT `product_category_ibfk_2` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`);

diky

jtousek
Člen | 951
+
0
-

Není náhodou v tabulce products PK sloupec jen id? V definici toho FK máš product_id.

EDIT: Jestli to není ono tak zkus $product->product->product_name; (odstranit to „s“).

Editoval jtousek (9. 5. 2012 19:03)

simPod
Člen | 383
+
0
-

nevim co znamena presne pk a fk, mirne tusim

v tabulce products je nazev sloupce product_id

jtousek
Člen | 951
+
0
-

pk = primary key

fk = foreign key

Ta chyba reference not found znamená že to nenašlo referenci (fk) pro „products“.

Nejsem si teď jistý podle čeho se vyhodnocuje název té reference – buď se ořízne to _id, pak by to bylo „product“, nebo se použije název tabulky, pak by to bylo „products“. Ta druhá varianta mi ale nedává smysl, protože mohu mít dva fk různého významu do jedné tabulky. Takže si myslím, že správná reference je právě jen „product“ a ne „products“.

Editoval jtousek (9. 5. 2012 19:18)

simPod
Člen | 383
+
0
-

tak to bude to oriznuti _id

je lepsi ten foreach delat v prezenteru nebo modulu?

jtousek
Člen | 951
+
0
-

Myslel jsi asi v modelu, ne v modulu.

Jednoznačná odpověď neexistuje, záleží na situaci/přístupu/implementaci/… Často se taky foreach dělá až v šabloně.

simPod
Člen | 383
+
0
-

ano, myslel jsem model

a pak jeste jedna vec: jak na ordering?

function getProductsByCategory($id) {
    $array = array();
    $query = $this->connection->table('categories')->get($id)->related('product_category');
    foreach ($query as $row) {
        $row = $row->product;
        array_push($array, $row);
    }
    return ksort($array);
}

a chci vysledek seradit treba podle nazvu produktu: products.product_name

EDIT: resp. jak na to zavolat napr. limit(), order()where('published',1)

dik

Editoval simPod (9. 5. 2012 20:24)

jtousek
Člen | 951
+
0
-

Hmm tak tos mě dostal. :-D

Zkusil bych tohle:

$this->connection->table('categories')->get($id)->related('product_category')->order('product:product_name');

Když to nepůjde tak místo té dvojtečky zkus tečku. Když ani to nepůjde tak nevím. ;-)

simPod
Člen | 383
+
0
-

ono to s tou teckou fakt funguje :) diky moc

simPod
Člen | 383
+
0
-

jeste mam dotaz.

jak tohle

$this->connection->table('categories')->get($id)->related('product_category');

rozsirit o where('id', array(1,2,3))

get() mi vybere jenom jeden radek, jednu kategorii, ja jich potrebuju vic…

diky

vvoody
Člen | 910
+
0
-

Related sa da samozrejme pouzit len na riadok (ActiveRow). Mozne riesenie:

foreach($this->connection->table('categories')->where('id', array(1,2,3)) as $row){
	$row->related('product_category');
}
simPod
Člen | 383
+
0
-

diky to funguje.

je nejaky jednodussi zpusob jak na vysledku udelat neco ve stylu order() ?

ja si vsechny rows nahazel do array a pak to rovnam pres usort, ktery volam skrz tridu, abych mu mohl predat dalsi parametry. je to komplikovanejsi, funguje to, ale asi jedinne reseni (?)

jtousek
Člen | 951
+
0
-

Teď tě nechápu. Tohle nejde?

foreach($this->connection->table('categories')->where('id', array(1,2,3))->order(...) as $row){
        $row->related('product_category');
}
simPod
Člen | 383
+
0
-

no, tim seradim jen kategorie

ted delam tohle:

//Model
function getProductsByCategory(...){
	$array = array();
	foreach ($this->connection->table('categories')->where('id', $id)->order('product_price') as $query) {
            $query = $query->related('product_category');

            foreach ($query as $row) {
                $row = $row->product;
                array_push($array, $row);
            }
        }
        usort($array, array(new CmpClass($order), "call"));
}

//class
class CmpClass extends ProductModel {

	private $order;

	function __construct($order) {
		$this->order = $order;
	}

	function call($a, $b) {
		return $this->cmp($a, $b, $this->order);
	}

	function cmp($a, $b, $order) {
		return strcmp($b[$order[0]], $a[$order[0]]);
	}
}
jtousek
Člen | 951
+
0
-

Hmm…

foreach($this->connection->table('categories')->where('id', array(1,2,3)) as $row){
        $row->related('product_category')->order('product.order'); //nebo jiný sloupec
}

Editoval jtousek (13. 5. 2012 16:23)

simPod
Člen | 383
+
0
-

a tohle funguje pro kazdou kategorii zvlast, protoze ten foreach bere kazdou kategorii postupne

jtousek
Člen | 951
+
0
-

Tak jinak, co je vlastně cílem?