Implement relationships in ORM (#288)

This commit is contained in:
An Tao 2019-10-26 23:44:31 +08:00 committed by GitHub
parent 4868aa964d
commit 876e21f492
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 748 additions and 94 deletions

View File

@ -31,44 +31,51 @@
using namespace drogon_ctl;
std::string nameTransform(const std::string &origName, bool isType)
static std::map<std::string, std::vector<Relationship>> getRelationships(
const Json::Value &relationships)
{
auto str = origName;
std::transform(str.begin(), str.end(), str.begin(), tolower);
std::string::size_type startPos = 0;
std::string::size_type pos;
std::string ret;
do
std::map<std::string, std::vector<Relationship>> ret;
auto enabled = relationships.get("enabled", false).asBool();
if (!enabled)
return ret;
auto items = relationships["items"];
if (items.isNull())
return ret;
if (!items.isArray())
{
pos = str.find("_", startPos);
if (pos == std::string::npos)
std::cerr << "items must be an array\n";
exit(1);
}
for (auto &relationship : items)
{
try
{
pos = str.find(".", startPos);
Relationship r(relationship);
ret[r.originalTableName()].push_back(r);
if (r.enableReverse() &&
r.originalTableName() != r.targetTableName())
{
auto reverse = r.reverse();
ret[reverse.originalTableName()].push_back(reverse);
}
}
if (pos != std::string::npos)
ret += str.substr(startPos, pos - startPos);
else
catch (const std::runtime_error &e)
{
ret += str.substr(startPos);
break;
std::cerr << e.what() << std::endl;
exit(1);
}
while (str[pos] == '_' || str[pos] == '.')
pos++;
if (str[pos] >= 'a' && str[pos] <= 'z')
str[pos] += ('A' - 'a');
startPos = pos;
} while (1);
if (isType && ret[0] >= 'a' && ret[0] <= 'z')
ret[0] += ('A' - 'a');
}
return ret;
}
#if USE_POSTGRESQL
void create_model::createModelClassFromPG(const std::string &path,
const DbClientPtr &client,
const std::string &tableName,
const std::string &schema,
const Json::Value &restfulApiConfig)
void create_model::createModelClassFromPG(
const std::string &path,
const DbClientPtr &client,
const std::string &tableName,
const std::string &schema,
const Json::Value &restfulApiConfig,
const std::vector<Relationship> &relationships)
{
auto className = nameTransform(tableName, true);
HttpViewData data;
@ -78,6 +85,7 @@ void create_model::createModelClassFromPG(const std::string &path,
data["primaryKeyName"] = "";
data["dbName"] = _dbname;
data["rdbms"] = std::string("postgresql");
data["relationships"] = relationships;
if (schema != "public")
{
data["schema"] = schema;
@ -290,10 +298,12 @@ void create_model::createModelClassFromPG(const std::string &path,
sourceFile << templ->genText(data);
createRestfulAPIController(data, restfulApiConfig);
}
void create_model::createModelFromPG(const std::string &path,
const DbClientPtr &client,
const std::string &schema,
const Json::Value &restfulApiConfig)
void create_model::createModelFromPG(
const std::string &path,
const DbClientPtr &client,
const std::string &schema,
const Json::Value &restfulApiConfig,
std::map<std::string, std::vector<Relationship>> &relationships)
{
*client << "SELECT a.oid,"
"a.relname AS name,"
@ -307,13 +317,22 @@ void create_model::createModelFromPG(const std::string &path,
<< schema << Mode::Blocking >>
[&](bool isNull,
size_t oid,
const std::string &tableName,
std::string &&tableName,
const std::string &comment) {
if (!isNull)
{
std::transform(tableName.begin(),
tableName.end(),
tableName.begin(),
tolower);
std::cout << "table name:" << tableName << std::endl;
createModelClassFromPG(
path, client, tableName, schema, restfulApiConfig);
createModelClassFromPG(path,
client,
tableName,
schema,
restfulApiConfig,
relationships[tableName]);
}
} >>
[](const DrogonDbException &e) {
@ -328,7 +347,8 @@ void create_model::createModelClassFromMysql(
const std::string &path,
const DbClientPtr &client,
const std::string &tableName,
const Json::Value &restfulApiConfig)
const Json::Value &restfulApiConfig,
const std::vector<Relationship> &relationships)
{
auto className = nameTransform(tableName, true);
HttpViewData data;
@ -338,6 +358,7 @@ void create_model::createModelClassFromMysql(
data["primaryKeyName"] = "";
data["dbName"] = _dbname;
data["rdbms"] = std::string("mysql");
data["relationships"] = relationships;
std::vector<ColumnInfo> cols;
int i = 0;
*client << "desc " + tableName << Mode::Blocking >>
@ -464,25 +485,31 @@ void create_model::createModelClassFromMysql(
sourceFile << templ->genText(data);
createRestfulAPIController(data, restfulApiConfig);
}
void create_model::createModelFromMysql(const std::string &path,
const DbClientPtr &client,
const Json::Value &restfulApiConfig)
void create_model::createModelFromMysql(
const std::string &path,
const DbClientPtr &client,
const Json::Value &restfulApiConfig,
std::map<std::string, std::vector<Relationship>> &relationships)
{
*client << "show tables" << Mode::Blocking >>
[&](bool isNull, const std::string &tableName) {
if (!isNull)
{
std::cout << "table name:" << tableName << std::endl;
createModelClassFromMysql(path,
client,
tableName,
restfulApiConfig);
}
} >>
[](const DrogonDbException &e) {
std::cerr << e.base().what() << std::endl;
exit(1);
};
*client << "show tables" << Mode::Blocking >> [&](bool isNull,
std::string &&tableName) {
if (!isNull)
{
std::transform(tableName.begin(),
tableName.end(),
tableName.begin(),
tolower);
std::cout << "table name:" << tableName << std::endl;
createModelClassFromMysql(path,
client,
tableName,
restfulApiConfig,
relationships[tableName]);
}
} >> [](const DrogonDbException &e) {
std::cerr << e.base().what() << std::endl;
exit(1);
};
}
#endif
#if USE_SQLITE3
@ -490,7 +517,8 @@ void create_model::createModelClassFromSqlite3(
const std::string &path,
const DbClientPtr &client,
const std::string &tableName,
const Json::Value &restfulApiConfig)
const Json::Value &restfulApiConfig,
const std::vector<Relationship> &relationships)
{
*client << "SELECT sql FROM sqlite_master WHERE name=? and (type='table' "
"or type='view');"
@ -514,6 +542,8 @@ void create_model::createModelClassFromSqlite3(
data["primaryKeyName"] = "";
data["dbName"] = std::string("sqlite3");
data["rdbms"] = std::string("sqlite3");
data["relationships"] = relationships;
// std::cout << sql << std::endl;
auto columns = utils::splitString(sql, ",");
int i = 0;
@ -624,21 +654,28 @@ void create_model::createModelClassFromSqlite3(
exit(1);
};
}
void create_model::createModelFromSqlite3(const std::string &path,
const DbClientPtr &client,
const Json::Value &restfulApiConfig)
void create_model::createModelFromSqlite3(
const std::string &path,
const DbClientPtr &client,
const Json::Value &restfulApiConfig,
std::map<std::string, std::vector<Relationship>> &relationships)
{
*client << "SELECT name FROM sqlite_master WHERE name!='sqlite_sequence' "
"and (type='table' or type='view') ORDER BY name;"
<< Mode::Blocking >>
[=](bool isNull, const std::string &tableName) {
[&](bool isNull, std::string &&tableName) mutable {
if (!isNull)
{
std::transform(tableName.begin(),
tableName.end(),
tableName.begin(),
tolower);
std::cout << "table name:" << tableName << std::endl;
createModelClassFromSqlite3(path,
client,
tableName,
restfulApiConfig);
restfulApiConfig,
relationships[tableName]);
}
} >>
[](const DrogonDbException &e) {
@ -655,6 +692,7 @@ void create_model::createModel(const std::string &path,
auto dbType = config.get("rdbms", "no dbms").asString();
std::transform(dbType.begin(), dbType.end(), dbType.begin(), tolower);
auto restfulApiConfig = config["restful_api_controllers"];
auto relationships = getRelationships(config["relationships"]);
if (dbType == "postgresql")
{
#if USE_POSTGRESQL
@ -714,22 +752,35 @@ void create_model::createModel(const std::string &path,
{
auto tables = config["tables"];
if (!tables || tables.size() == 0)
createModelFromPG(path, client, schema, restfulApiConfig);
createModelFromPG(
path, client, schema, restfulApiConfig, relationships);
else
{
for (int i = 0; i < (int)tables.size(); i++)
{
auto tableName = tables[i].asString();
std::transform(tableName.begin(),
tableName.end(),
tableName.begin(),
tolower);
std::cout << "table name:" << tableName << std::endl;
createModelClassFromPG(
path, client, tableName, schema, restfulApiConfig);
createModelClassFromPG(path,
client,
tableName,
schema,
restfulApiConfig,
relationships[tableName]);
}
}
}
else
{
createModelClassFromPG(
path, client, singleModelName, schema, restfulApiConfig);
createModelClassFromPG(path,
client,
singleModelName,
schema,
restfulApiConfig,
relationships[singleModelName]);
}
#else
std::cerr
@ -795,17 +846,25 @@ void create_model::createModel(const std::string &path,
{
auto tables = config["tables"];
if (!tables || tables.size() == 0)
createModelFromMysql(path, client, restfulApiConfig);
createModelFromMysql(path,
client,
restfulApiConfig,
relationships);
else
{
for (int i = 0; i < (int)tables.size(); i++)
{
auto tableName = tables[i].asString();
std::transform(tableName.begin(),
tableName.end(),
tableName.begin(),
tolower);
std::cout << "table name:" << tableName << std::endl;
createModelClassFromMysql(path,
client,
tableName,
restfulApiConfig);
restfulApiConfig,
relationships[tableName]);
}
}
}
@ -814,7 +873,8 @@ void create_model::createModel(const std::string &path,
createModelClassFromMysql(path,
client,
singleModelName,
restfulApiConfig);
restfulApiConfig,
relationships[singleModelName]);
}
#else
@ -858,17 +918,25 @@ void create_model::createModel(const std::string &path,
{
auto tables = config["tables"];
if (!tables || tables.size() == 0)
createModelFromSqlite3(path, client, restfulApiConfig);
createModelFromSqlite3(path,
client,
restfulApiConfig,
relationships);
else
{
for (int i = 0; i < (int)tables.size(); i++)
{
auto tableName = tables[i].asString();
std::transform(tableName.begin(),
tableName.end(),
tableName.begin(),
tolower);
std::cout << "table name:" << tableName << std::endl;
createModelClassFromSqlite3(path,
client,
tableName,
restfulApiConfig);
restfulApiConfig,
relationships[tableName]);
}
}
}
@ -877,7 +945,8 @@ void create_model::createModel(const std::string &path,
createModelClassFromSqlite3(path,
client,
singleModelName,
restfulApiConfig);
restfulApiConfig,
relationships[singleModelName]);
}
#else

View File

@ -22,6 +22,7 @@ using namespace drogon::orm;
#include <drogon/DrObject.h>
#include "CommandHandler.h"
#include <string>
#include <algorithm>
using namespace drogon;
@ -43,6 +44,222 @@ struct ColumnInfo
bool _hasDefaultVal = false;
};
inline std::string nameTransform(const std::string &origName, bool isType)
{
auto str = origName;
std::transform(str.begin(), str.end(), str.begin(), tolower);
std::string::size_type startPos = 0;
std::string::size_type pos;
std::string ret;
do
{
pos = str.find("_", startPos);
if (pos == std::string::npos)
{
pos = str.find(".", startPos);
}
if (pos != std::string::npos)
ret += str.substr(startPos, pos - startPos);
else
{
ret += str.substr(startPos);
break;
}
while (str[pos] == '_' || str[pos] == '.')
pos++;
if (str[pos] >= 'a' && str[pos] <= 'z')
str[pos] += ('A' - 'a');
startPos = pos;
} while (1);
if (isType && ret[0] >= 'a' && ret[0] <= 'z')
ret[0] += ('A' - 'a');
return ret;
}
class PivotTable
{
public:
PivotTable() = default;
PivotTable(const Json::Value &json)
{
_tableName = json.get("table_name", "").asString();
if (_tableName.empty())
{
throw std::runtime_error("table_name can't be empty");
}
_originalKey = json.get("original_key", "").asString();
if (_originalKey.empty())
{
throw std::runtime_error("original_key can't be empty");
}
_targetKey = json.get("target_key", "").asString();
if (_targetKey.empty())
{
throw std::runtime_error("target_key can't be empty");
}
}
PivotTable reverse() const
{
PivotTable pivot;
pivot._tableName = _tableName;
pivot._originalKey = _targetKey;
pivot._targetKey = _originalKey;
return pivot;
}
const std::string &tableName() const
{
return _tableName;
}
const std::string &originalKey() const
{
return _originalKey;
}
const std::string &targetKey() const
{
return _targetKey;
}
private:
std::string _tableName;
std::string _originalKey;
std::string _targetKey;
};
class Relationship
{
public:
enum class Type
{
HasOne,
HasMany,
ManyToMany
};
Relationship(const Json::Value &relationship)
{
auto type = relationship.get("type", "has one").asString();
if (type == "has one")
{
_type = Relationship::Type::HasOne;
}
else if (type == "has many")
{
_type = Relationship::Type::HasMany;
}
else if (type == "many to many")
{
_type = Relationship::Type::ManyToMany;
}
else
{
char message[128];
sprintf(message, "Invalid relationship type: %s", type.data());
throw std::runtime_error(message);
}
_originalTableName =
relationship.get("original_table_name", "").asString();
if (_originalTableName.empty())
{
throw std::runtime_error("original_table_name can't be empty");
}
_originalKey = relationship.get("original_key", "").asString();
if (_originalKey.empty())
{
throw std::runtime_error("original_key can't be empty");
}
_originalTableAlias =
relationship.get("original_table_alias", "").asString();
_targetTableName = relationship.get("target_table_name", "").asString();
if (_targetTableName.empty())
{
throw std::runtime_error("target_table_name can't be empty");
}
_targetKey = relationship.get("target_key", "").asString();
if (_targetKey.empty())
{
throw std::runtime_error("target_key can't be empty");
}
_targetTableAlias =
relationship.get("target_table_alias", "").asString();
_enableReverse = relationship.get("enable_reverse", false).asBool();
if (_type == Type::ManyToMany)
{
auto &pivot = relationship["pivot_table"];
if (pivot.isNull())
{
throw std::runtime_error(
"ManyToMany relationship needs a pivot table");
}
_pivotTable = PivotTable(pivot);
}
}
Relationship() = default;
Relationship reverse() const
{
Relationship r;
if (_type == Type::HasMany)
{
r._type = Type::HasOne;
}
else
{
r._type = _type;
}
r._originalTableName = _targetTableName;
r._originalTableAlias = _targetTableAlias;
r._originalKey = _targetKey;
r._targetTableName = _originalTableName;
r._targetTableAlias = _originalTableAlias;
r._targetKey = _originalKey;
r._enableReverse = _enableReverse;
r._pivotTable = _pivotTable.reverse();
return r;
}
Type type() const
{
return _type;
}
bool enableReverse() const
{
return _enableReverse;
}
const std::string &originalTableName() const
{
return _originalTableName;
}
const std::string &originalTableAlias() const
{
return _originalTableAlias;
}
const std::string &originalKey() const
{
return _originalKey;
}
const std::string &targetTableName() const
{
return _targetTableName;
}
const std::string &targetTableAlias() const
{
return _targetTableAlias;
}
const std::string &targetKey() const
{
return _targetKey;
}
const PivotTable &pivotTable() const
{
return _pivotTable;
}
private:
Type _type = Type::HasOne;
std::string _originalTableName;
std::string _originalTableAlias;
std::string _targetTableName;
std::string _targetTableAlias;
std::string _originalKey;
std::string _targetKey;
bool _enableReverse = false;
PivotTable _pivotTable;
};
class create_model : public DrObject<create_model>, public CommandHandler
{
public:
@ -63,29 +280,40 @@ class create_model : public DrObject<create_model>, public CommandHandler
const DbClientPtr &client,
const std::string &tableName,
const std::string &schema,
const Json::Value &restfulApiConfig);
void createModelFromPG(const std::string &path,
const DbClientPtr &client,
const std::string &schema,
const Json::Value &restfulApiConfig);
const Json::Value &restfulApiConfig,
const std::vector<Relationship> &relationships);
void createModelFromPG(
const std::string &path,
const DbClientPtr &client,
const std::string &schema,
const Json::Value &restfulApiConfig,
std::map<std::string, std::vector<Relationship>> &relationships);
#endif
#if USE_MYSQL
void createModelClassFromMysql(const std::string &path,
const DbClientPtr &client,
const std::string &tableName,
const Json::Value &restfulApiConfig);
void createModelFromMysql(const std::string &path,
const DbClientPtr &client,
const Json::Value &restfulApiConfig);
void createModelClassFromMysql(
const std::string &path,
const DbClientPtr &client,
const std::string &tableName,
const Json::Value &restfulApiConfig,
const std::vector<Relationship> &relationships);
void createModelFromMysql(
const std::string &path,
const DbClientPtr &client,
const Json::Value &restfulApiConfig,
std::map<std::string, std::vector<Relationship>> &relationships);
#endif
#if USE_SQLITE3
void createModelClassFromSqlite3(const std::string &path,
const DbClientPtr &client,
const std::string &tableName,
const Json::Value &restfulApiConfig);
void createModelFromSqlite3(const std::string &path,
const DbClientPtr &client,
const Json::Value &restfulApiConfig);
void createModelClassFromSqlite3(
const std::string &path,
const DbClientPtr &client,
const std::string &tableName,
const Json::Value &restfulApiConfig,
const std::vector<Relationship> &relationships);
void createModelFromSqlite3(
const std::string &path,
const DbClientPtr &client,
const Json::Value &restfulApiConfig,
std::map<std::string, std::vector<Relationship>> &relationships);
#endif
void createRestfulAPIController(const DrTemplateData &tableInfo,
const Json::Value &restfulApiConfig);

View File

@ -9,6 +9,35 @@ using namespace drogon_ctl;
*/
#include "[[className]].h"
<%c++
auto &relationships=@@.get<std::vector<Relationship>>("relationships");
auto rdbms=@@.get<std::string>("rdbms");
for(auto &relationship : relationships)
{
if(relationship.type() == Relationship::Type::HasOne ||
relationship.type() == Relationship::Type::HasMany ||
relationship.type() == Relationship::Type::ManyToMany)
{
auto &name=relationship.targetTableName();
auto relationshipClassName=nameTransform(name, true);
auto originalClassName=nameTransform(relationship.originalTableName(),true);
if(relationshipClassName!=originalClassName)
{
%>
#include "{%relationshipClassName%}.h"
<%c++
}
}
if(relationship.type() == Relationship::Type::ManyToMany)
{
auto &pivotTableName=relationship.pivotTable().tableName();
auto pivotTableClassName=nameTransform(pivotTableName, true);
%>
#include "{%pivotTableClassName%}.h"
<%c++
}
}
%>
#include <drogon/utils/Utilities.h>
#include <string>
<%c++
@ -1299,3 +1328,177 @@ if(!col._notNull){%>
}
return true;
}
<%c++
for(auto &relationship : relationships)
{
if(relationship.targetKey().empty() || relationship.originalKey().empty())
{
continue;
}
auto &name=relationship.targetTableName();
auto relationshipClassName=nameTransform(name, true);
auto relationshipValName=nameTransform(name, false);
auto alias=relationship.targetTableAlias();
auto aliasValName=nameTransform(alias, false);
if(relationship.type() == Relationship::Type::HasOne)
{
if(!alias.empty())
{
if(alias[0] <= 'z' && alias[0] >= 'a')
{
alias[0] += ('A' - 'a');
}
std::string alind(alias.length(), ' ');
%>
void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
{%indentStr%} {%alind%} const std::function<void({%relationshipClassName%})> &rcb,
{%indentStr%} {%alind%} const ExceptionCallback &ecb) const
<%c++
}
else
{
std::string relationshipClassInde(relationshipClassName.length(), ' ');
%>
void [[className]]::get{%relationshipClassName%}(const DbClientPtr &clientPtr,
{%indentStr%} {%relationshipClassInde%} const std::function<void({%relationshipClassName%})> &rcb,
{%indentStr%} {%relationshipClassInde%} const ExceptionCallback &ecb) const
<%c++
}
%>
{
const static std::string sql = "select * from {%name%} where {%relationship.targetKey()%} = <%c++
if(rdbms=="postgresql")
{
$$<<"$1";
}
else
{
$$<<"?";
}%>";
*clientPtr << sql
<< *_{%nameTransform(relationship.originalKey(), false)%}
>> [rcb = std::move(rcb), ecb](const Result &r){
if (r.size() == 0)
{
ecb(UnexpectedRows("0 rows found"));
}
else if (r.size() > 1)
{
ecb(UnexpectedRows("Found more than one row"));
}
else
{
rcb({%relationshipClassName%}(r[0]));
}
}
>> ecb;
}
<%c++
}
else if(relationship.type() == Relationship::Type::HasMany)
{
if(!alias.empty())
{
if(alias[0] <= 'z' && alias[0] >= 'a')
{
alias[0] += ('A' - 'a');
}
std::string alind(alias.length(), ' ');
%>
void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
{%indentStr%} {%alind%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,
{%indentStr%} {%alind%} const ExceptionCallback &ecb) const
<%c++
}
else
{
std::string relationshipClassInde(relationshipClassName.length(), ' ');
%>
void [[className]]::get{%relationshipClassName%}(const DbClientPtr &clientPtr,
{%indentStr%} {%relationshipClassInde%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,
{%indentStr%} {%relationshipClassInde%} const ExceptionCallback &ecb) const
<%c++
}
%>
{
const static std::string sql = "select * from {%name%} where {%relationship.targetKey()%} = <%c++
if(rdbms=="postgresql")
{
$$<<"$1";
}
else
{
$$<<"?";
}%>";
*clientPtr << sql
<< *_{%nameTransform(relationship.originalKey(), false)%}
>> [rcb = std::move(rcb)](const Result &r){
std::vector<{%relationshipClassName%}> ret;
ret.reserve(ret.size());
for (auto const &row : r)
{
ret.emplace_back({%relationshipClassName%}(row));
}
rcb(ret);
}
>> ecb;
}
<%c++
}
else if(relationship.type() == Relationship::Type::ManyToMany)
{
auto &pivotTableName=relationship.pivotTable().tableName();
auto pivotTableClassName=nameTransform(pivotTableName, true);
auto &pivotOriginalKey=relationship.pivotTable().originalKey();
auto &pivotTargetKey=relationship.pivotTable().targetKey();
if(!alias.empty())
{
if(alias[0] <= 'z' && alias[0] >= 'a')
{
alias[0] += ('A' - 'a');
}
std::string alind(alias.length(), ' ');
%>
void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
{%indentStr%} {%alind%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>) &rcb,
{%indentStr%} {%alind%} const ExceptionCallback &ecb) const
<%c++
}
else
{
std::string relationshipClassInde(relationshipClassName.length(), ' ');
%>
void [[className]]::get{%relationshipClassName%}(const DbClientPtr &clientPtr,
{%indentStr%} {%relationshipClassInde%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb,
{%indentStr%} {%relationshipClassInde%} const ExceptionCallback &ecb) const
<%c++
}
%>
{
const static std::string sql = "select * from {%name%},{%pivotTableName%} where {%pivotTableName%}.{%pivotOriginalKey%} = <%c++
if(rdbms=="postgresql")
{
$$<<"$1";
}
else
{
$$<<"?";
}%> and {%pivotTableName%}.{%pivotTargetKey%} = {%name%}.{%relationship.targetKey()%}";
*clientPtr << sql
<< *_{%nameTransform(relationship.originalKey(), false)%}
>> [rcb = std::move(rcb)](const Result &r){
std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> ret;
ret.reserve(ret.size());
for (auto const &row : r)
{
ret.emplace_back(std::pair<{%relationshipClassName%},{%pivotTableClassName%}>(
{%relationshipClassName%}(row),{%pivotTableClassName%}(row,{%relationshipClassName%}::getColumnNumber())));
}
rcb(ret);
}
>> ecb;
}
<%c++
}
}
%>

View File

@ -25,7 +25,14 @@ using namespace drogon_ctl;
#include <iostream>
using namespace drogon::orm;
namespace drogon
{
namespace orm
{
class DbClient;
typedef std::shared_ptr<DbClient> DbClientPtr;
}
}
namespace drogon_model
{
namespace [[dbName]]
@ -37,7 +44,25 @@ if(!schema.empty())
$$<<"namespace "<<schema<<"\n";
$$<<"{\n";
}
auto &relationships=@@.get<std::vector<Relationship>>("relationships");
for(auto &relationship : relationships)
{
auto &name=relationship.targetTableName();
auto relationshipClassName=nameTransform(name, true);
%>
class {%relationshipClassName%};
<%c++
if(relationship.type() == Relationship::Type::ManyToMany)
{
auto &pivotTableName=relationship.pivotTable().tableName();
auto pivotTableClassName=nameTransform(pivotTableName, true);
%>
class {%pivotTableClassName%};
<%c++
}
}
%>
class [[className]]
{
public:
@ -172,7 +197,98 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
Json::Value toJson() const;
Json::Value toMasqueradedJson(const std::vector<std::string> &pMasqueradingVector) const;
/// Relationship interfaces
<%c++
for(auto &relationship : relationships)
{
if(relationship.targetKey().empty() || relationship.originalKey().empty())
{
continue;
}
auto &name=relationship.targetTableName();
auto relationshipClassName=nameTransform(name, true);
auto relationshipValName=nameTransform(name, false);
auto alias=relationship.targetTableAlias();
auto aliasValName=nameTransform(alias, false);
if(relationship.type() == Relationship::Type::HasOne)
{
if(!alias.empty())
{
if(alias[0] <= 'z' && alias[0] >= 'a')
{
alias[0] += ('A' - 'a');
}
std::string alind(alias.length(), ' ');
%>
void get{%alias%}(const DbClientPtr &clientPtr,
{%alind%} const std::function<void({%relationshipClassName%})> &rcb,
{%alind%} const ExceptionCallback &ecb) const;
<%c++
}
else
{
std::string relationshipClassInde(relationshipClassName.length(), ' ');
%>
void get{%relationshipClassName%}(const DbClientPtr &clientPtr,
{%relationshipClassInde%} const std::function<void({%relationshipClassName%})> &rcb,
{%relationshipClassInde%} const ExceptionCallback &ecb) const;
<%c++
}
}
else if(relationship.type() == Relationship::Type::HasMany)
{
if(!alias.empty())
{
if(alias[0] <= 'z' && alias[0] >= 'a')
{
alias[0] += ('A' - 'a');
}
std::string alind(alias.length(), ' ');
%>
void get{%alias%}(const DbClientPtr &clientPtr,
{%alind%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,
{%alind%} const ExceptionCallback &ecb) const;
<%c++
}
else
{
std::string relationshipClassInde(relationshipClassName.length(), ' ');
%>
void get{%relationshipClassName%}(const DbClientPtr &clientPtr,
{%relationshipClassInde%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,
{%relationshipClassInde%} const ExceptionCallback &ecb) const;
<%c++
}
}
else if(relationship.type() == Relationship::Type::ManyToMany)
{
auto &pivotTableName=relationship.pivotTable().tableName();
auto pivotTableClassName=nameTransform(pivotTableName, true);
if(!alias.empty())
{
if(alias[0] <= 'z' && alias[0] >= 'a')
{
alias[0] += ('A' - 'a');
}
std::string alind(alias.length(), ' ');
%>
void get{%alias%}(const DbClientPtr &clientPtr,
{%alind%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb,
{%alind%} const ExceptionCallback &ecb) const;
<%c++
}
else
{
std::string relationshipClassInde(relationshipClassName.length(), ' ');
%>
void get{%relationshipClassName%}(const DbClientPtr &clientPtr,
{%relationshipClassInde%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb,
{%relationshipClassInde%} const ExceptionCallback &ecb) const;
<%c++
}
}
}
%>
private:
friend Mapper<[[className]]>;
static const std::vector<std::string> &insertColumns() noexcept;
@ -200,7 +316,6 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
};
static const std::vector<MetaData> _metaData;
bool _dirtyFlag[{%cols.size()%}]={ false };
public:
static const std::string &sqlForFindingByPrimaryKey()
{

View File

@ -10,13 +10,52 @@
//dbname: Database name;
"dbname": "",
//schema: valid for postgreSQL, "public" by default;
"schema":"public",
"schema": "public",
//user: User name
"user": "",
//password: Password
"passwd": "",
//table: An array of tables to be modelized. if the array is empty, all revealed tables are modelized.
"tables": [],
"relationships": {
"enabled": false,
"items": [{
"type": "has one",
"original_table_name": "products",
"original_table_alias": "product",
"original_key": "id",
"target_table_name": "skus",
"target_table_alias": "SKU",
"target_key": "product_id",
"enable_reverse": true
},
{
"type": "has many",
"original_table_name": "products",
"original_table_alias": "product",
"original_key": "id",
"target_table_name": "reviews",
"target_table_alias": "",
"target_key": "product_id",
"enable_reverse": true
},
{
"type": "many to many",
"original_table_name": "products",
"original_table_alias": "",
"original_key": "id",
"pivot_table": {
"table_name": "carts_products",
"original_key": "product_id",
"target_key": "cart_id"
},
"target_table_name": "carts",
"target_table_alias": "",
"target_key": "id",
"enable_reverse": true
}
]
},
"restful_api_controllers": {
"enabled": false,
// resource_uri: The URI to access the resource, the default value