menu - PHP और mysql के माध्यम से मेनू के असीमित स्तर को कैसे बनाया जाए




(8)

यहां इस समस्या के लिए " एक क्वेरी , कोई पुनरावर्ती " समाधान का "डेवलपर-अनुकूल" संस्करण नहीं है।

एसक्यूएल :

SELECT id, parent_id, title, link, position FROM menu_item ORDER BY parent_id, position;

PHP :

$html = '';
$parent = 0;
$parent_stack = array();

// $items contains the results of the SQL query
$children = array();
foreach ( $items as $item )
    $children[$item['parent_id']][] = $item;

while ( ( $option = each( $children[$parent] ) ) || ( $parent > 0 ) )
{
    if ( !empty( $option ) )
    {
        // 1) The item contains children:
        // store current parent in the stack, and update current parent
        if ( !empty( $children[$option['value']['id']] ) )
        {
            $html .= '<li>' . $option['value']['title'] . '</li>';
            $html .= '<ul>'; 
            array_push( $parent_stack, $parent );
            $parent = $option['value']['id'];
        }
        // 2) The item does not contain children
        else
            $html .= '<li>' . $option['value']['title'] . '</li>';
    }
    // 3) Current parent has no more children:
    // jump back to the previous menu level
    else
    {
        $html .= '</ul>';
        $parent = array_pop( $parent_stack );
    }
}

// At this point, the HTML is already built
echo $html;

आपको $ parent_stack चर के उपयोग को समझने की आवश्यकता है

यह एक "LIFO" स्टैक है (अंतिम इन्स, पहले आउट) - विकिपीडिया लेख की छवि में एक हज़ार शब्द हैं: http://en.wikipedia.org/wiki/LIFO_%28computing%29

जब एक मेनू विकल्प के उप-विकल्प होते हैं, तो हम स्टैक में इसकी मूल आईडी को संग्रहीत करते हैं:

array_push( $parent_stack, $parent );

और फिर, हम तत्काल $ माता-पिता को अपडेट करते हैं, इसे वर्तमान मेनू विकल्प आईडी बनाकर:

$parent = $option['value']['id'];

इसके बाद हमने इसके सभी उप-विकल्प को रोक दिया, हम पिछले स्तर पर वापस लौट सकते हैं:

$parent = array_pop( $parent_stack );

यही कारण है कि हमने मूल आईडी को स्टैक में संग्रहीत किया है!

मेरा सुझाव है: ऊपर दिए गए कोड स्निपेट पर गौर करें, और इसे समझें।

प्रश्न का स्वागत है!

इस दृष्टिकोण में मैं देख रहा हूं कि यह एक अनंत लूप में प्रवेश करने के जोखिम को समाप्त करता है, जो तब हो सकता है जब recursion उपयोग किया जाता है।

खैर, मेरे मेनू का मेन्यू बनाने के लिए मैं इस तरह से एक डीबी संरचना का उपयोग करता हूं

  2  Services                  0
  3  Photo Gallery             0
  4  Home                      0
  5  Feedback                  0
  6  FAQs                      0
  7  News & Events             0
  8  Testimonials              0
 81  FACN                      0
 83  Organisation Structure   81
 84  Constitution             81
 85  Council                  81
 86  IFAWPCA                  81
 87  Services                 81
 88  Publications             81

मौजूदा सबमेनू के लिए एक अन्य उपमेनू प्रदान करने के लिए मैं अपने माता पिता के आईडी को मूल फ़ील्ड के मान के रूप में निर्दिष्ट करता हूं। माता पिता 0 का मतलब शीर्ष मेनू है

अब एक अन्य सबमेनू में सबमेनू बनाते समय समस्या नहीं है

अब यह तरीका है कि मैं शीर्ष मेनू के लिए सबमेनू ले आता हूं

<ul class="topmenu">
    <? $list = $obj -> childmenu($parentid); 
        //this list contains the array of submenu under $parendid
        foreach($list as $menu) {
            extract($menu);
            echo '<li><a href="#">'.$name.'</a></li>';
        }
    ?>
</ul>

मैं क्या करना चाहता हूं

मैं यह जानना चाहता हूं कि कोई नया मेनू दूसरे बाल मेनू है या नहीं

और मैं जब तक यह उपलब्ध हर बच्चे मेनू को खोजता है, तब तक जांच करना जारी रखना चाहता हूं

और मैं इस तरह अपने विशेष सूची आइटम के अंदर अपने बाल मेनू प्रदर्शित करना चाहता हूँ

<ul>       
       <li><a href="#">Home</a>
        <ul class="submenu">
           ........ <!-- Its sub menu -->
           </ul>
       </li>
</ul>

इसके लिए आपको रिकर्सिव फ़ंक्शन का उपयोग करना होगा। तकनीकी तौर पर, ऐसा करने के कुछ तरीके हैं, लेकिन पुनर्कथन वास्तव में सबसे अच्छा विकल्प है

यहां बताया गया है कि यह कैसे काम करेगा:

function drawMenu ($listOfItems) {
    echo "<ul>";
    foreach ($listOfItems as $item) {
        echo "<li>" . $item->name;
        if ($item->hasChildren()) {
            drawMenu($item->getChildren()); // here is the recursion
        }
        echo "</li>";
    }
    echo "</ul>";
}

$item की संपत्तियां और विधियां सिर्फ उदाहरण हैं, और मैं इन्हें आपको लागू करने के लिए इसे लागू करने के लिए छोड़ दूँगा, लेकिन मुझे लगता है कि यह संदेश पूरे भर में आता है।


मैं एक पुनरावर्ती समारोह का उपयोग करेगा

मैं जानता हूँ कि यह आपके कोड की तरह बिल्कुल नहीं है, लेकिन मुझे लगता है कि यदि आप पुनरावर्तन को समझते हैं तो आप सामान्य अवधारणा प्राप्त कर सकते हैं। अगर आपको रिकर्सन समझ में नहीं आता है http://en.wikipedia.org/wiki/Recursion_(computer_science)

$list = new List();

function print_menu($list) {

    echo '<ul>';
    foreach($list as $item) {
        echo '<li><a href="#">' . $item->name . '</a>';
        if($item->has_child) {
            print_menu($item);
        }
        echo '</li>';
    }
    echo '</ul>';
}

वैकल्पिक पाठ http://i.imagehost.org/0934/product_hier.jpg http://pastie.org/969286

drop table if exists product;

create table product
(
prod_id smallint unsigned not null auto_increment primary key,
name varchar(255) not null,
parent_id smallint unsigned null,
key (parent_id)
)engine = innodb;


insert into product (name, parent_id) values
('Products',null), 
   ('Systems & Bundles',1), 
   ('Components',1), 
      ('Processors',3), 
      ('Motherboards',3), 
        ('AMD',5), 
        ('Intel',5), 
           ('Intel LGA1366',7);


delimiter ;

drop procedure if exists product_hier;

delimiter #

create procedure product_hier
(
in p_prod_id smallint unsigned
)
begin

declare v_done tinyint unsigned default 0;
declare v_depth smallint unsigned default 0;

create temporary table hier(
 parent_id smallint unsigned, 
 prod_id smallint unsigned, 
 depth smallint unsigned default 0
)engine = memory;

insert into hier select parent_id, prod_id, v_depth from product where prod_id = p_prod_id;

/* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */

create temporary table tmp engine=memory select * from hier;

while not v_done do

    if exists( select 1 from product p inner join hier on p.parent_id = hier.prod_id and hier.depth = v_depth) then

        insert into hier 
            select p.parent_id, p.prod_id,  v_depth + 1 from product p 
            inner join tmp on p.parent_id = tmp.prod_id and tmp.depth = v_depth;

        set v_depth = v_depth + 1;          

        truncate table tmp;
        insert into tmp select * from hier where depth = v_depth;

    else
        set v_done = 1;
    end if;

end while;

select 
 p.prod_id,
 p.name as prod_name,
 b.prod_id as parent_prod_id,
 b.name as parent_prod_name,
 hier.depth
from 
 hier
inner join product p on hier.prod_id = p.prod_id
inner join product b on hier.parent_id = b.prod_id
order by
 hier.depth, hier.prod_id;

drop temporary table if exists hier;
drop temporary table if exists tmp;

end #

delimiter ;


call product_hier(3);

call product_hier(5);

मैं सुझाव दूंगा कि आप पूर्व-आदेशित वृक्ष ट्रांज़ैशल पर गौर करें। इस मुद्दे पर इस पर एक लेख है:

MySQL में पदानुक्रमित डेटा प्रबंधित करना

प्रभावी रूप से, आप प्रत्येक पृष्ठ को 'नोड' के रूप में लेते हैं प्रत्येक नोड के माता-पिता के लिए एक संदर्भ है। जब आप नोड्स का लेआउट बदलते हैं (एक बच्चा जोड़ें, नोड्स ले जाएं, आदि), तो आप प्रत्येक नोड के लिए एक 'बाएं' और 'दाएं' मान की पुनर्गणना करते हैं (उपरोक्त आलेख यह पीएचपी में स्रोत कोड के लिंक के साथ महान विवरण में बताता है )। आप के साथ क्या समाप्त होता है, यह बहुत जल्दी निर्धारित करने की क्षमता है कि यदि कोई नोड किसी अन्य नोड का प्रत्यक्ष या अप्रत्यक्ष बच्चा है, साथ ही किसी दिए गए नोड के सभी बच्चे नोड प्राप्त करें।


http://pastebin.com/ariBn3pE

आपको रिकर्सन का उपयोग करने की आवश्यकता है, लेकिन मेरा दृष्टिकोण अलग है, मैंने प्रत्येक मेनू को अलग-अलग संभाल करने के लिए एक वर्ग बनाया है, फिर परिणाम के लिए पूछताछ किया है और प्रत्येक तत्व के समूह में उनके माता-पिता के अनुसार, प्रत्येक स्तर के अनुसार, और फिर सभी वस्तुओं एक में ... पूर्ण कोड के लिए पेस्टबिन को चेक करें


मैंने इस तरह से पाया, वाई फ़्रेमवर्क के साथ काम करना

$children = array();

foreach($model as $k => $item){
    if(empty($item->cn_id_menu_padre))
        $children[$item->cn_id] = $item->attributes;
    else
        $children[$item->cn_id_menu_padre]['hijos'][] = $item->attributes;
}

foreach($children as $k=>$child){
    if(array_key_exists('hijos',$child))
    {
        echo 'li y dentro ul<br>';
        foreach($child['hijos'] as $hijo){
            echo 'li<br>';
        }
    }
    else
        echo 'li<br>';
}

अगर आपको एक और स्तर की आवश्यकता है, तो आप बच्चों के hijos_de_hijos को दूसरे स्तर पर बना सकते हैं जैसे कि hijos_de_hijos और तुलना करें तो अगर hijos_de_hijos

ओह, ज़ाहिर है, तुलना करने के लिए अगर cn_id_menu_padre खाली है, डेटाबेस में मान null होना चाहिए।


कॉलेशन utf8mb4_unicode_ci साथ वर्ण सेट utf8mb4 का उपयोग करना सबसे अच्छा है।

चरित्र सेट, utf8 , केवल यूटीएफ -8 कोड बिंदुओं की एक छोटी राशि का समर्थन करता है, संभावित पात्रों में से लगभग 6%। utf8 केवल मूल बहुभाषी विमान (बीएमपी) का समर्थन करता है। वहां 16 अन्य विमान हैं। प्रत्येक विमान में 65,536 वर्ण होते हैं। utf8mb4 सभी 17 विमानों का समर्थन करता है।

MySQL 4 बाइट यूटीएफ -8 अक्षरों को दूषित कर देगा जिसके परिणामस्वरूप दूषित डेटा होगा।

utf8mb4 वर्ण सेट को 2010-03-24 को MySQL 5.5.3 में पेश किया गया था।

नए चरित्र सेट का उपयोग करने के लिए आवश्यक कुछ बदलाव छोटे नहीं हैं:

  • परिवर्तन आपके एप्लिकेशन डेटाबेस एडाप्टर में किए जाने की आवश्यकता हो सकती है।
  • बदलावों को my.cnf में करने की आवश्यकता होगी, जिसमें चरित्र सेट, संयोजन और barracuda में innodb_file_format को स्विच करना शामिल है
  • एसक्यूएल निर्माण कथन में शामिल करने की आवश्यकता हो सकती है: ROW_FORMAT=DYNAMIC
    • VARCHAR (1 9 2) और बड़ी पर इंडेक्स के लिए डायनामिक की आवश्यकता है।

नोट: Antelope से Antelope स्विच करने के लिए, एक बार से अधिक MySQL सेवा को पुनरारंभ करने की आवश्यकता हो सकती है। innodb_file_format_max तब तक नहीं बदलता जब तक MySQL सेवा को पुनरारंभ नहीं किया जाता है: innodb_file_format = barracuda

MySQL पुराने Antelope इनो डीबी फ़ाइल प्रारूप का उपयोग करता है। Barracuda गतिशील पंक्ति स्वरूपों का समर्थन करता है, यदि आप charset पर स्विच करने के बाद अनुक्रमणिका और कुंजी बनाने के लिए SQL त्रुटियों को हिट नहीं करना चाहते हैं, तो आपको आवश्यकता होगी: utf8mb4

  • # 170 9 - इंडेक्स कॉलम आकार बहुत बड़ा है। अधिकतम कॉलम आकार 767 बाइट्स है।
  • # 1071 - निर्दिष्ट कुंजी बहुत लंबी थी; अधिकतम कुंजी लंबाई 767 बाइट है

निम्नलिखित परिदृश्य का परीक्षण MySQL 5.6.17 पर किया गया है: डिफ़ॉल्ट रूप से, MySQL इस तरह कॉन्फ़िगर किया गया है:

SHOW VARIABLES;

innodb_large_prefix = OFF
innodb_file_format = Antelope

अपनी MySQL सेवा को रोकें और अपने मौजूदा my.cnf में विकल्प जोड़ें:

[client]
default-character-set= utf8mb4

[mysqld]
explicit_defaults_for_timestamp = true
innodb_large_prefix = true
innodb_file_format = barracuda
innodb_file_format_max = barracuda
innodb_file_per_table = true

# Character collation
character_set_server=utf8mb4
collation_server=utf8mb4_unicode_ci

उदाहरण एसक्यूएल कथन बनाएं:

CREATE TABLE Contacts (
 id INT AUTO_INCREMENT NOT NULL,
 ownerId INT DEFAULT NULL,
 created timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
 modified timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 contact VARCHAR(640) NOT NULL,
 prefix VARCHAR(128) NOT NULL,
 first VARCHAR(128) NOT NULL,
 middle VARCHAR(128) NOT NULL,
 last VARCHAR(128) NOT NULL,
 suffix VARCHAR(128) NOT NULL,
 notes MEDIUMTEXT NOT NULL,
 INDEX IDX_CA367725E05EFD25 (ownerId),
 INDEX created (created),
 INDEX modified_idx (modified),
 INDEX contact_idx (contact),
 PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB ROW_FORMAT=DYNAMIC;
  • यदि आप CREATE कथन से ROW_FORMAT=DYNAMIC हटा दिए गए हैं, तो आप INDEX contact_idx (contact) लिए उत्पन्न त्रुटि # 170 9 देख सकते हैं।

नोट: contact पर पहले 128 वर्णों तक सीमित करने के लिए इंडेक्स को बदलना ROW_FORMAT=DYNAMIC का उपयोग ROW_FORMAT=DYNAMIC साथ करने के लिए आवश्यकता को समाप्त करता है

INDEX contact_idx (contact(128)),

यह भी ध्यान दें: जब यह कहता है कि क्षेत्र का आकार VARCHAR(128) , तो यह 128 बाइट्स नहीं है। आप 128, 4 बाइट वर्ण या 128, 1 बाइट वर्णों का उपयोग कर सकते हैं।

इस INSERT कथन में 2 पंक्ति में 4 बाइट 'पू' वर्ण होना चाहिए:

INSERT INTO `Contacts` (`id`, `ownerId`, `created`, `modified`, `contact`, `prefix`, `first`, `middle`, `last`, `suffix`, `notes`) VALUES
(1, NULL, '0000-00-00 00:00:00', '2014-08-25 03:00:36', '1234567890', '12345678901234567890', '1234567890123456789012345678901234567890', '1234567890123456789012345678901234567890', '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678', '', ''),
(2, NULL, '0000-00-00 00:00:00', '2014-08-25 03:05:57', 'poo', '12345678901234567890', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '', ''),
(3, NULL, '0000-00-00 00:00:00', '2014-08-25 03:05:57', 'poo', '12345678901234567890', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '123💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '', '');

आप last कॉलम द्वारा उपयोग की जाने वाली जगह की मात्रा देख सकते हैं:

mysql> SELECT BIT_LENGTH(`last`), CHAR_LENGTH(`last`) FROM `Contacts`;
+--------------------+---------------------+
| BIT_LENGTH(`last`) | CHAR_LENGTH(`last`) |
+--------------------+---------------------+
|               1024 |                 128 | -- All characters are ASCII
|               4096 |                 128 | -- All characters are 4 bytes
|               4024 |                 128 | -- 3 characters are ASCII, 125 are 4 bytes
+--------------------+---------------------+

अपने डेटाबेस एडाप्टर में, आप अपने कनेक्शन के लिए वर्णमाला और संयोजन सेट करना चाह सकते हैं:

SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'

PHP में, यह सेट किया जाएगा: \PDO::MYSQL_ATTR_INIT_COMMAND

संदर्भ:





php mysql menu