teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

5

PHP修正

2017/08/03 07:14

投稿

yambejp
yambejp

スコア117965

answer CHANGED
@@ -128,7 +128,11 @@
128
128
 
129
129
  SELECT name,level,url,link,(SELECT COUNT(*)-1 FROM tbl AS t2 WHERE t2.l BETWEEN t1.l AND t1.r ) AS child FROM tbl AS t1 ORDER BY l
130
130
 
131
+ ※PHP修正
132
+
131
133
  ```PHP
134
+
135
+
132
136
  try{
133
137
  /* PDOの接続は自身の環境に合わせて下さい */
134
138
  $pdo = new PDO($dsn, $user,$password);
@@ -164,7 +168,6 @@
164
168
  }
165
169
  print "<li>";
166
170
  if($row["link"]==1 and $row["url"]!==""){
167
- /* item-arrowクラスの付け方がおかしかったので修正 */
168
171
  $class=$row["child"]>0?" class=\"item-arrow\"":"";
169
172
  print "<a href=\"{$row["url"]}\"{$class}>{$row["name"]}</a>";
170
173
  }else{
@@ -176,14 +179,18 @@
176
179
  }
177
180
  if($l2>$l3){
178
181
  print "</li>".PHP_EOL;
179
- for($i=$l2-$l3-1;$i>0;$i--){
182
+ for($i=0;$i<$l2-$l3-1;$i++){
180
- print str_repeat(" ",($i)*$indent);
183
+ print str_repeat(" ",($l2-$i-1)*$indent);
181
184
  print "</ul>".PHP_EOL;
182
- print str_repeat(" ",($i-1)*$indent);
185
+ print str_repeat(" ",($l2-$i-2)*$indent);
183
186
  print "</li>".PHP_EOL;
184
187
  }
185
188
  print str_repeat(" ",($l3)*$indent);
186
189
  print "</ul>".PHP_EOL;
190
+ if($l3>0){
191
+ print str_repeat(" ",($l3-1)*$indent);
192
+ print "</li>".PHP_EOL;
193
+ }
187
194
  }
188
195
  $l1=$l2;
189
196
  }

4

typo

2017/08/03 07:14

投稿

yambejp
yambejp

スコア117965

answer CHANGED
@@ -130,18 +130,17 @@
130
130
 
131
131
  ```PHP
132
132
  try{
133
- $dsn = 'mysql:host=localhost; dbname=mydb;charset=utf8;';
134
- $user = 'root';
135
- $password = '*****';
133
+ /* PDOの接続は自身の環境に合わせて下さい */
136
134
  $pdo = new PDO($dsn, $user,$password);
137
- $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
138
- $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
139
- $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);
140
135
  $sql ='SELECT name,level,url,link,(SELECT COUNT(*) FROM tbl AS t2 WHERE t2.l BETWEEN t1.l AND t1.r AND t2.level=t1.level+1) AS child FROM tbl AS t1 ORDER BY l';
136
+ /* 今回prepare処理は不要なのでqueryに変更しておきます
141
137
  $stmt = $pdo->prepare($sql);
142
138
  $stmt->execute([]);
139
+ */
140
+ $stmt = $pdo->query($sql);
143
141
  $rows=$stmt->fetchAll(PDO::FETCH_ASSOC);
142
+ /*当初gett_reeになっていたので修正します*/
144
- print gett_ree($rows);
143
+ print get_tree($rows);
145
144
  }catch(PDOException $e){
146
145
  die($e->getMessage());
147
146
  }

3

修正

2017/08/02 04:41

投稿

yambejp
yambejp

スコア117965

answer CHANGED
@@ -165,7 +165,9 @@
165
165
  }
166
166
  print "<li>";
167
167
  if($row["link"]==1 and $row["url"]!==""){
168
+ /* item-arrowクラスの付け方がおかしかったので修正 */
169
+ $class=$row["child"]>0?" class=\"item-arrow\"":"";
168
- print "<a href=\"{$row["url"]}\" class=\"item-arrow\">{$row["name"]}</a>";
170
+ print "<a href=\"{$row["url"]}\"{$class}>{$row["name"]}</a>";
169
171
  }else{
170
172
  print $row["name"];
171
173
  }

2

PHP

2017/08/02 02:59

投稿

yambejp
yambejp

スコア117965

answer CHANGED
@@ -112,4 +112,84 @@
112
112
  SELECT CONCAT( REPEAT( "__", LEVEL -1 ) , name ) AS name
113
113
  FROM area
114
114
  ORDER BY l
115
- ```
115
+ ```
116
+
117
+ # PHPへの書き出し
118
+ 以前作った分の流用なので若干インデントのバグがありますが
119
+ 出力するHTML文としてはさほど問題ないはずです
120
+ ※ポイント
121
+ - classの指定など最大限当初例示いただいたHTMLにあわせたつもりですが
122
+ おかしかったらうまく調整してください
123
+ - linkが1でURLが空でなければアンカーが貼られます
124
+ あえてリンクを表示したくない場合レコードのlinkを0にするなどテストしてみてください
125
+ - ただしhtmlspecialcharやurlencodeなどかけてないので注意
126
+ - 一応直下の要素数をカウントして表示しておきました
127
+ ちなみに、以下SQL文にすればカテゴリを含むすべての子要素数が表示されます
128
+
129
+ SELECT name,level,url,link,(SELECT COUNT(*)-1 FROM tbl AS t2 WHERE t2.l BETWEEN t1.l AND t1.r ) AS child FROM tbl AS t1 ORDER BY l
130
+
131
+ ```PHP
132
+ try{
133
+ $dsn = 'mysql:host=localhost; dbname=mydb;charset=utf8;';
134
+ $user = 'root';
135
+ $password = '*****';
136
+ $pdo = new PDO($dsn, $user,$password);
137
+ $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
138
+ $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
139
+ $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);
140
+ $sql ='SELECT name,level,url,link,(SELECT COUNT(*) FROM tbl AS t2 WHERE t2.l BETWEEN t1.l AND t1.r AND t2.level=t1.level+1) AS child FROM tbl AS t1 ORDER BY l';
141
+ $stmt = $pdo->prepare($sql);
142
+ $stmt->execute([]);
143
+ $rows=$stmt->fetchAll(PDO::FETCH_ASSOC);
144
+ print gett_ree($rows);
145
+ }catch(PDOException $e){
146
+ die($e->getMessage());
147
+ }
148
+
149
+ function get_tree($rows){
150
+ $indent=3;
151
+ $l1=0;
152
+ ob_start();
153
+ foreach($rows as $key=>$row){
154
+ $l2=$row["level"];
155
+ $l3=($key<count($rows)-1)?$rows[$key+1]["level"]:0;
156
+ if($l1>=$l2){
157
+ print str_repeat(" ",($l2-1)*$indent);
158
+ }
159
+ if($l1<$l2){
160
+ print PHP_EOL;
161
+ print str_repeat(" ",($l2-1)*$indent);
162
+ $class=$row["level"]>1?" class=\"level{$row["level"]}\"":"";
163
+ print "<ul{$class}>".PHP_EOL;
164
+ print str_repeat(" ",($l2-1)*$indent);
165
+ }
166
+ print "<li>";
167
+ if($row["link"]==1 and $row["url"]!==""){
168
+ print "<a href=\"{$row["url"]}\" class=\"item-arrow\">{$row["name"]}</a>";
169
+ }else{
170
+ print $row["name"];
171
+ }
172
+ if($row["child"]>0) print "(".$row["child"].")";
173
+ if($l2==$l3){
174
+ print "</li>".PHP_EOL;;
175
+ }
176
+ if($l2>$l3){
177
+ print "</li>".PHP_EOL;
178
+ for($i=$l2-$l3-1;$i>0;$i--){
179
+ print str_repeat(" ",($i)*$indent);
180
+ print "</ul>".PHP_EOL;
181
+ print str_repeat(" ",($i-1)*$indent);
182
+ print "</li>".PHP_EOL;
183
+ }
184
+ print str_repeat(" ",($l3)*$indent);
185
+ print "</ul>".PHP_EOL;
186
+ }
187
+ $l1=$l2;
188
+ }
189
+ $str=ob_get_contents();
190
+ ob_end_clean();
191
+ return $str;
192
+ }
193
+
194
+ ```
195
+

1

別テーブル

2017/08/02 02:25

投稿

yambejp
yambejp

スコア117965

answer CHANGED
@@ -3,4 +3,113 @@
3
3
  ご提示の階層は「経路列挙モデル」に近いですが、カテゴリをカラムで管理
4
4
  しているため、拡張性がわるく持ち方としていまいちです。
5
5
 
6
- 今回のテーブル構成はマストでしょうか?
6
+ 今回のテーブル構成はマストでしょうか?
7
+
8
+ # 隣接リストモデルから入れ子集合モデルへ
9
+
10
+ - 隣接リストモデル
11
+ ```SQL
12
+ create table tbl(
13
+ id int not null unique,
14
+ parent_id int null,
15
+ name varchar(30),
16
+ url varchar(100),
17
+ link tinyint,
18
+ level int not null default 0,
19
+ l int null,
20
+ r int null
21
+ );
22
+ ```
23
+ - 入れ子集合モデルへのコンバータ
24
+ ```SQL
25
+ DROP PROCEDURE IF EXISTS SET_LR;
26
+ DELIMITER //
27
+ CREATE PROCEDURE SET_LR()
28
+ BEGIN
29
+ DECLARE a INT DEFAULT 0;
30
+ DECLARE done INT DEFAULT 0;
31
+ DECLARE CUR CURSOR FOR
32
+ SELECT id FROM tbl WHERE level=0 ORDER BY parent_id ASC,id DESC;
33
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
34
+ UPDATE tbl SET level=0,l=0,r=0;
35
+ /* level,l,rを初期化*/
36
+
37
+ UPDATE tbl SET level=1,l=(SELECT @a:=@a+1 FROM (SELECT @a:=0) AS sub),r=@a:=@a+1
38
+ WHERE parent_id IS NULL
39
+ ORDER BY id;
40
+ /* 先頭データのlevelを1とし、l,rを1ずつ足していく*/
41
+
42
+ OPEN CUR;
43
+ REPEAT
44
+ FETCH CUR INTO a;
45
+ IF NOT done THEN
46
+ SET @id=a;
47
+ SET @sql='UPDATE tbl as a1,tbl as a2,tbl as a3 SET a1.l=a2.l+1,a1.r=a2.l+2,a1.level=a2.level+1,a2.r=a2.r+2,a3.r=a3.r+2 WHERE a1.parent_id=a2.id AND a2.l<a3.r AND a1.id=?';
48
+ /* 子のlに親の1+1、子のrに親のl+2,子のlevelに親のlevel+1親のrを2増やす、親のlより大きいrを2増やす */
49
+
50
+ PREPARE stmt from @sql;
51
+ EXECUTE stmt USING @id;
52
+ SET @sql='UPDATE tbl as a1,tbl as a2,tbl as a3 SET a3.l=a3.l+2 WHERE a1.parent_id=a2.id AND a2.l<a3.l and a3.id!=a1.id AND a1.id=?';
53
+ /* 親の1より大きいlの内、子以外のものを2増やす */
54
+
55
+ PREPARE stmt from @sql;
56
+ EXECUTE stmt USING @id;
57
+ END IF;
58
+ UNTIL done END REPEAT;
59
+ CLOSE CUR;
60
+ END
61
+ //
62
+ DELIMITER ;
63
+ ```
64
+ - データ投入
65
+ ```SQL
66
+ insert into tbl(id,parent_id,name,url,link) values
67
+ ( 1,null,'A','#',1),
68
+ ( 2,null,'B','#',1),
69
+ ( 3,1,'AA','#',1),
70
+ ( 4,1,'BB','#',1),
71
+ ( 5,2,'AA','#',1),
72
+ ( 6,2,'BB','#',1),
73
+ ( 7,3,'AAA','#',1),
74
+ ( 8,3,'BBB','#',1),
75
+ ( 9,3,'CCC','#',1),
76
+ (10,4,'BBB','#',1),
77
+ (11,4,'CCC','#',1),
78
+ (12,5,'AAA','#',1),
79
+ (13,6,'AAA','#',1),
80
+ (14,6,'BBB','#',1),
81
+ (15,7,'A-ct4','#',1),
82
+ (16,8,'A-ct4','#',1),
83
+ (17,8,'B-ct4','#',1),
84
+ (18,9,'A-ct4','#',1),
85
+ (19,10,'A-ct4','#',1),
86
+ (20,11,'B-ct4','#',1),
87
+ (21,12,'A-ct4','#',1),
88
+ (22,13,'B-ct4','#',1),
89
+ (23,14,'A-ct4','#',1),
90
+ (24,21,'A-ct5','#',1),
91
+ (25,22,'B-ct5','#',1),
92
+ (26,23,'C-ct5','#',1),
93
+ (27,24,'A-ct6','#',1),
94
+ (28,15,'name1001','www',1),
95
+ (29,16,'name1002','www',1),
96
+ (30,17,'name1003','www',1),
97
+ (31,18,'name1004','www',1),
98
+ (32,19,'name1005','www',1),
99
+ (33,19,'name1006','www',1),
100
+ (34,20,'name1007','www',1),
101
+ (35,27,'name1008','www',1),
102
+ (36,25,'name1009','www',1),
103
+ (37,26,'name1010','www',1);
104
+ ```
105
+ - コンバート
106
+ ```SQL
107
+ CALL SET_LR;
108
+ ```
109
+ # 階層状況の確認
110
+
111
+ ```SQL
112
+ SELECT CONCAT( REPEAT( "__", LEVEL -1 ) , name ) AS name
113
+ FROM area
114
+ ORDER BY l
115
+ ```