ver.2 では、まだDB複製機能が実装されていません。(※)
今回は、 DB複製機能を作ってみました。
DBを複製して、どんどんDBを作りたいと思っている方は、ぜひ参考にしてください。
※ 今後、実装される可能性があります。
前提
ver.2 のページ機能にプログラムを設置し、画面上から複製元のアプリIDとDB IDを入力できるようにしています。
PHP / API
PHPの処理を書きます。
[API_KEY]は、各環境に合わせた値の設定をお願いします。
変更が必要な箇所は、API_KEY の部分のみです。
<?php
define("API_URL", "https://api.spiral-platform.com/v1/");
define("API_KEY", "");
$commonBase = CommonBase::getInstance();
if($SPIRAL->getParam("appID")){
$SPIRAL->setTHValue("postAction", true);
$SPIRAL->setTHValue("error", false);
// DBを取得
$addData = $commonBase->apiCurlAction("GET","apps/".$SPIRAL->getParam("appID")."/dbs/".$SPIRAL->getParam("dbID"));
// 名称をチェックして、入力がない場合は、 C_ をつける
if($SPIRAL->getParam("appName")){
$newDbData["name"] = $SPIRAL->getParam("appName");
}else{
$newDbData["name"] = "c_".$addData["name"];
}
if($SPIRAL->getParam("displayName")){
$newDbData["displayName"] = $SPIRAL->getParam("displayName");
}else{
$newDbData["displayName"] = "c_".$addData["displayName"];
}
// 複製用DBに説明をセット
$newDbData["description"] = $addData["description"];
foreach($addData["fields"] as $key => $val){
// 取得したDBの情報で複製に不要な項目は削除
unset($addData["fields"][$key]["id"]);
unset($addData["fields"][$key]["fieldOrder"]);
unset($addData["fields"][$key]["size"]);
if($addData["fields"][$key]["type"] == "reference"){
$addData["fields"][$key]["referenceDbId"] = $addData["fields"][$key]["referenceDb"]["id"];
$addData["fields"][$key]["referenceLabelFieldId"] = $addData["fields"][$key]["referenceLabelField"]["id"];
unset($addData["fields"][$key]["referenceDb"]);
unset($addData["fields"][$key]["referenceLabelField"]);
}
}
// 複製用DBに複製元の情報をセット
$newDbData["fields"] = $addData["fields"];
// DB複製
$newDB = $commonBase->apiCurlAction("POST","apps/".$SPIRAL->getParam("appID")."/dbs",$newDbData);
if(isset($newDB["status"]) && $newDB["status"] != 200){
// エラー時
$SPIRAL->setTHValue("error", true);
$SPIRAL->setTHValue("errorLocation", "DB Create");
$SPIRAL->setTHValue("apiMessage", print_r($newDB, true));
}
}else{
$SPIRAL->setTHValue("postAction", false);
}
//------------------------------
// 共通用モジュール
//------------------------------
class CommonBase
{
/**
* シングルトンインスタンス
* @var UserManager
*/
protected static $singleton;
public function __construct()
{
if (self::$singleton) {
throw new Exception('must be singleton');
}
self::$singleton = $this;
}
/**
* シングルトンインスタンスを返す
* @return UserManager
*/
public static function getInstance()
{
if (!self::$singleton) {
return new CommonBase();
} else {
return self::$singleton;
}
}
/**
* V2用 API送信ロジック
* @return Result
*/
function apiCurlAction($method, $addUrlPass, $data = null)
{
$header = array(
"Authorization:Bearer " . API_KEY,
"Content-Type:application/json",
"X-Spiral-Api-Version: 1.1",
);
// curl
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_URL, API_URL . $addUrlPass);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
if ($method == "POST") {
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
}
if ($method == "PATCH") {
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
}
if ($method == "DELETE") {
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
}
if ($method == "GET") {
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
}
$response = curl_exec($curl);
if (curl_errno($curl)) echo curl_error($curl);
curl_close($curl);
return json_decode($response, true);
}
}
HTML(Thymeleaf)
アプリIDとDB IDを入力するための画面 と アプリ複製の処理結果を表示させる処理を記載
HTML(Thymeleaf)<div class="box_con">
<h1>ver.2 DB 複製ツール</h1>
<form method="post" action="#">
<table class="formTable">
<tr>
<th>アプリID<span>必須</span></th>
<td><input size="30" type="text" name="appID" required="required" value="" /></td>
</tr>
<tr>
<th>DB ID<span>必須</span></th>
<td><input size="30" type="text" name="dbID" required="required" value="" /></td>
</tr>
<tr>
<th>表示名</th>
<td>
<input size="30" type="text" name="displayName" maxlength="64" placeholder="未入力の場合、コピー元の表示名の接頭に「c_」がついた表示名になります。" /><br />
<span class="contents-note">* 既存アプリの表示名と重複不可</span><br />
</td>
</tr>
<tr>
<th>識別名</th>
<td>
<input size="30" type="text" name="appName" pattern="^[0-9A-Z_a-z]+$" maxlength="64" placeholder="未入力の場合、コピー元の識別名の接頭に「c_」がついた識別名になります。" /><br />
<span class="contents-note">* アルファベット大文字・小文字、数字、アンダースコアのみ使用可能</span><br />
<span class="contents-note">* 最初の文字はアルファベット</span><br />
<span class="contents-note">* 既存アプリの識別名と重複不可</span><br />
</td>
</tr>
</table><br />
<p class="btn">
<span><input type="submit" value=" 確認 " /></span>
</p>
</form>
<div th:if="${!cp.result.isSuccess}">
<p th:text="${cp.result.errorMessage}">error message</p>
</div>
<div class="con_pri" style="margin-top: 20px;">
<div th:if="${cp.result.value['postAction'] == true}" class="box_pri">
<div class="box_tori">
<h4>実行結果</h4>
<!--/* エラー時 */-->
<p th:if="${cp.result.value['error']}" th:text="${cp.result.value['errorLocation']}"></p>
<p th:if="${cp.result.value['error']}" th:text="${cp.result.value['apiMessage']}"></p>
<!--/* 成功時 */-->
<p th:if="${!cp.result.value['error']}" class="txt">success!</p>
</div>
</div>
</div>
</div>
CSS
一応、見やすいようにCSSも追加
CSS/*リセットcss↓*/
html {
overflow-y: scroll;
}
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, fieldset, input, textarea, p, blockquote, th, td {
margin: 0;
padding: 0;
}
address, caption, cite, code, dfn, em, strong, th, var {
font-style: normal;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
caption, th {
text-align: left;
color: #217599;
}
q:before, q:after {
content: '';
}
object, embed {
vertical-align: top;
}
hr, legend {
display: none;
}
h1, h2, h3, h4, h5, h6 {
font-size: 100%;
}
img, abbr, acronym, fieldset {
border: 0;
}
li {
list-style-type: none;
}
sup {
vertical-align: super;
font-size: 0.5em;
}
img {
vertical-align: top;
}
i {
font-style: normal;
}
/*----リセットcss*----/
/*デザインcss↓*/
.box_con {
max-width: 900px;
margin: 0 auto;
}
@media only screen and (max-width: 768px) {
.box_con {
width: 95%;
}
}
.box_con form {
width: 100%;
}
.box_con form table {
width: 100%;
}
.box_con form table tr {
position: relative;
}
.box_con form table tr:after {
content: "";
position: absolute;
width: 100%;
left: 0;
bottom: 0;
height: 1px;
border-bottom: dotted #cdcdcd 1px;
}
.box_con form table tr th {
width: 30%;
font-weight: normal;
padding: 1em .5em;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
@media only screen and (max-width: 768px) {
.box_con form table tr th {
text-align: center;
width: 100%;
display: block;
background: #97ae88;
padding: .8em .2em;
color: #fff;
}
}
.box_con form table tr th span {
background: #cd6f55;
padding: 0 .3em;
color: #fff;
margin-left: .5em;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.box_con form table tr td {
padding: 1em .5em;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
@media only screen and (max-width: 768px) {
.box_con form table tr td {
padding: 1.5em .5em;
display: block;
width: 100%;
}
}
.box_con form table tr .box_br {
display: block;
}
.box_con form table tr select {
border: 1px solid #97ae88;
}
.box_con form table tr label input {
cursor: pointer;
display: none;
vertical-align: middle;
}
.box_con form table tr .radio02-input+label {
padding-left: 23px;
margin-right: 20px;
position: relative;
}
.box_con form table tr .radio02-input+label:before {
content: "";
display: block;
position: absolute;
top: 50%;
left: 0;
width: 16px;
height: 16px;
border: 1px solid #999;
border-radius: 50%;
-moz-transform: translateY(-50%);
-ms-transform: translateY(-50%);
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
}
.box_con form table tr .radio02-input:checked+label:after {
content: "";
display: block;
position: absolute;
top: 50%;
left: 3px;
width: 12px;
height: 12px;
background: #97ae88;
border-radius: 50%;
-moz-transform: translateY(-50%);
-ms-transform: translateY(-50%);
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
}
.box_con form table tr select, .box_con form table tr input, .box_con form table tr textarea {
width: 100%;
height: 3em;
padding: .5em;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.box_con form table tr textarea {
height: 10em;
}
/*プライバシーのデザインcss↓*/
.con_pri {
max-width: 700px;
margin: 0 auto;
}
@media only screen and (max-width: 768px) {
.con_pri {
width: 95%;
}
}
.con_pri .box_pri {
height: 300px;
overflow-y: scroll;
border: 1px solid #cdcdcd;
background: #f7f7f7;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin-top: 20px;
padding: 20px 55px;
}
@media only screen and (max-width: 768px) {
.con_pri .box_pri {
margin-top: 4%;
padding: 3%;
}
}
@media only screen and (min-width: 769px) and (max-width: 1024px) {
.con_pri .box_pri {
padding: 4%;
}
}
.con_pri .box_pri .box_tori {
text-align: left;
margin-top: 40px;
}
@media only screen and (max-width: 768px) {
.con_pri .box_pri .box_tori {
margin-top: 4%;
}
}
.con_pri .box_pri .box_tori h4 {
font-weight: normal;
margin-bottom: 30px;
font-size: 150%;
}
@media only screen and (max-width: 768px) {
.con_pri .box_pri .box_tori h4 {
margin-bottom: 4%;
}
}
.con_pri .box_pri .box_tori .txt {
padding: 0 20px;
}
@media only screen and (max-width: 768px) {
.con_pri .box_pri .box_tori .txt {
padding: 0;
}
}
.con_pri .box_pri .box_num {
margin-top: 30px;
}
@media only screen and (max-width: 768px) {
.con_pri .box_pri .box_num {
margin-top: 5%;
}
}
.con_pri .box_pri .box_num h4 {
font-weight: normal;
font-size: 113%;
}
.con_pri .box_pri .box_num .txt {
padding: 10px 0 0 20px;
}
@media only screen and (max-width: 768px) {
.con_pri .box_pri .box_num .txt {
padding: 3% 0 0 3%;
}
}
.box_check {
text-align: center;
margin: 1em auto;
}
.box_check label {
display: inline-block;
}
.box_check label span {
margin-left: .3em;
}
.btn {
text-align: center;
}
.btn input {
display: inline-block;
background: #eee;
padding: .5em 4em;
color: #000;
text-decoration: none;
cursor: pointer;
border: none;
-moz-transition: all 0.4s;
-o-transition: all 0.4s;
-webkit-transition: all 0.4s;
transition: all 0.4s;
}
.btn input:hover {
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70);
opacity: 0.7;
}
h1 {
border-bottom: solid 3px #69d3a6;
position: relative;
font-size: 2.75em;
color: #01a39d;
}
h1:after {
position: absolute;
content: " ";
display: block;
border-bottom: solid 3px #1aa3bf;
bottom: -3px;
width: 20%;
}
.contents-note {
font-size: 0.9em;
color: #777;
}
無理やり作ってしまっているところもあるので、DBの複製後はしっかりチェックをお願いいたします。
不具合やほかのやり方がある等あれば、下記の「コンテンツに関しての要望はこちら」からご連絡ください。