Flutter 新聞客戶端 - 15 strapi 資料建模 graphql 條件查詢排序

會煮咖啡的貓發表於2020-07-13

Flutter 新聞客戶端 - 15 strapi 資料建模 graphql 條件查詢排序

本節目標

  • portainer 容器管理工具
  • 資料庫設計過程
  • 資料庫設計目標、規範、習慣
  • graphql 條件查詢、排序
  • flutter 程式碼實現

視訊

www.bilibili.com/video/BV1jD…

Flutter 新聞客戶端 - 15 strapi 資料建模 graphql 條件查詢排序

程式碼

github.com/ducafecat/f…

strapi 執行環境網盤下載

  • 網盤

連結:pan.baidu.com/s/13Ujy2hzX… 密碼:yu82

  • 檔案
名稱 說明
strapi-docker-compose-00.zip 乾淨環境,已安裝 graphql 外掛
strapi-docker-compose-15.zip 15 課內容
  • 執行

需要用 docker-compose 啟動 賬號 admin 密碼 123456

# 啟動
docker-compose up -d --remove-orphans

# 關閉
docker-compose down
複製程式碼

工具

正文

安裝 portainer

docker run -d -p 9000:9000 \
    --restart=always \
    -v /var/run/docker.sock:/var/run/docker.sock \
    --name portainer-local \
    portainer/portainer
複製程式碼

設計資料模型

ER 圖 -> 設計正規化 -> 資料庫物理表

  • ER

Flutter 新聞客戶端 - 15 strapi 資料建模 graphql 條件查詢排序

  • 正規化

zh.wikipedia.org/wiki/資料庫規範化

  • 設計規範 表字首

sys* 系統、使用者、許可權 dict* 字典表 bus_ 業務

  • 設計資料 物件、屬性、關係

Flutter 新聞客戶端 - 15 strapi 資料建模 graphql 條件查詢排序

建立 strapi 資料型別

  • 建立外來鍵表 新聞分類

Flutter 新聞客戶端 - 15 strapi 資料建模 graphql 條件查詢排序

  • 建立外來鍵表 新聞頻道

Flutter 新聞客戶端 - 15 strapi 資料建模 graphql 條件查詢排序

  • 建立業務表 新聞內容

Flutter 新聞客戶端 - 15 strapi 資料建模 graphql 條件查詢排序

  • 建立連結外來鍵 新聞內容、分類、頻道

Flutter 新聞客戶端 - 15 strapi 資料建模 graphql 條件查詢排序

編寫 graphql 查詢

  • 新聞
query News($category_code: String) {
  busNews(where: { dict_categories: { code: $category_code } }) {
    title
    dict_channel {
      code
      title
      icon {
        url
      }
    }
    dict_categories {
      code
      title
    }
    author
    url
    addtime
    thumbnail {
      url
    }
  }
}
複製程式碼
  • 首頁
query pageIndex {
  # 分類
  dictCategories(sort: "sortNum:desc") {
    code
    title
  }
  # 頻道
  dictChannels(sort: "sortNum:desc") {
    code
    title
    icon {
      url
    }
  }
  # 熱點
  busNews(where: { dict_categories: { code: "news_hot" } }) {
    title
    dict_channel {
      code
      title
      icon {
        url
      }
    }
    dict_categories {
      code
      title
    }
    author
    url
    addtime
    thumbnail {
      url
    }
  }
}
複製程式碼

編寫 flutter 程式碼

  • 例項 entity

lib/common/entitys/gql_news.dart

// 首頁
class GqlIndexResponseEntity {
  GqlIndexResponseEntity({
    this.dictCategories,
    this.dictChannels,
    this.busNews,
  });

  List<DictCategoryEntity> dictCategories;
  List<DictChannelEntity> dictChannels;
  List<GqlNewsResponseEntity> busNews;

  factory GqlIndexResponseEntity.fromJson(Map<String, dynamic> json) =>
      GqlIndexResponseEntity(
        dictCategories: List<DictCategoryEntity>.from(
            json["dictCategories"].map((x) => DictCategoryEntity.fromJson(x))),
        dictChannels: List<DictChannelEntity>.from(
            json["dictChannels"].map((x) => DictChannelEntity.fromJson(x))),
        busNews: List<GqlNewsResponseEntity>.from(
            json["busNews"].map((x) => GqlNewsResponseEntity.fromJson(x))),
      );

  Map<String, dynamic> toJson() => {
        "dictCategories":
            List<dynamic>.from(dictCategories.map((x) => x.toJson())),
        "dictChannels": List<dynamic>.from(dictChannels.map((x) => x.toJson())),
        "busNews": List<dynamic>.from(busNews.map((x) => x.toJson())),
      };
}

// 新聞
class GqlNewsResponseEntity {
  GqlNewsResponseEntity({
    this.title,
    this.dictChannel,
    this.dictCategories,
    this.author,
    this.url,
    this.addtime,
    this.thumbnail,
  });

  String title;
  DictChannelEntity dictChannel;
  List<DictCategoryEntity> dictCategories;
  String author;
  String url;
  DateTime addtime;
  ThumbnailEntity thumbnail;

  factory GqlNewsResponseEntity.fromJson(Map<String, dynamic> json) =>
      GqlNewsResponseEntity(
        title: json["title"],
        dictChannel: DictChannelEntity.fromJson(json["dict_channel"]),
        dictCategories: List<DictCategoryEntity>.from(
            json["dict_categories"].map((x) => DictCategoryEntity.fromJson(x))),
        author: json["author"],
        url: json["url"],
        addtime: DateTime.parse(json["addtime"]),
        thumbnail: ThumbnailEntity.fromJson(json["thumbnail"]),
      );

  Map<String, dynamic> toJson() => {
        "title": title,
        "dict_channel": dictChannel.toJson(),
        "dict_categories":
            List<dynamic>.from(dictCategories.map((x) => x.toJson())),
        "author": author,
        "url": url,
        "addtime":
            "${addtime.year.toString().padLeft(4, '0')}-${addtime.month.toString().padLeft(2, '0')}-${addtime.day.toString().padLeft(2, '0')}",
        "ThumbnailEntity": thumbnail.toJson(),
      };
}

// 分類
class DictCategoryEntity {
  DictCategoryEntity({
    this.code,
    this.title,
  });

  String code;
  String title;

  factory DictCategoryEntity.fromJson(Map<String, dynamic> json) =>
      DictCategoryEntity(
        code: json["code"],
        title: json["title"],
      );

  Map<String, dynamic> toJson() => {
        "code": code,
        "title": title,
      };
}

// 頻道
class DictChannelEntity {
  DictChannelEntity({
    this.code,
    this.title,
    this.icon,
  });

  String code;
  String title;
  ThumbnailEntity icon;

  factory DictChannelEntity.fromJson(Map<String, dynamic> json) =>
      DictChannelEntity(
        code: json["code"],
        title: json["title"],
        icon: ThumbnailEntity.fromJson(json["icon"]),
      );

  Map<String, dynamic> toJson() => {
        "code": code,
        "title": title,
        "icon": icon.toJson(),
      };
}

// 圖
class ThumbnailEntity {
  ThumbnailEntity({
    this.url,
  });

  String url;

  factory ThumbnailEntity.fromJson(Map<String, dynamic> json) =>
      ThumbnailEntity(
        url: json["url"],
      );

  Map<String, dynamic> toJson() => {
        "url": url,
      };
}

複製程式碼
  • api

lib/common/apis/gql_news.dart

/// 新聞
class GqlNewsAPI {
  /// 首頁
  static Future<GqlIndexResponseEntity> indexPageInfo({
    @required BuildContext context,
    Map<String, dynamic> params,
  }) async {
    QueryResult response =
        await GraphqlClientUtil.query(context: context, schema: GQL_INDEX_PAGE);

    return GqlIndexResponseEntity.fromJson(response.data);
  }

  /// 翻頁
  static Future<List<GqlNewsResponseEntity>> newsPageList({
    @required BuildContext context,
    Map<String, dynamic> params,
  }) async {
    QueryResult response = await GraphqlClientUtil.query(
        context: context, schema: GQL_NEWS_LIST, variables: params);

    return response.data['busNews']
        .map<GqlNewsResponseEntity>(
            (item) => GqlNewsResponseEntity.fromJson(item))
        .toList();
  }
}
複製程式碼
  • 介面業務程式碼

lib/pages/main/main.dart

  GqlIndexResponseEntity _indexPageData; // 首頁資料

  // 讀取所有資料
  _loadAllData() async {
    _indexPageData = await GqlNewsAPI.indexPageInfo(context: context);
    ...

    if (mounted) {
      setState(() {});
    }
  }

  // 拉取推薦、新聞
  _loadNewsData(
    categoryCode, {
    bool refresh = false,
  }) async {
    _selCategoryCode = categoryCode;
    _newsPageList = await GqlNewsAPI.newsPageList(
        context: context, params: {"category_code": categoryCode});

    if (mounted) {
      setState(() {});
    }
  }

  ...
複製程式碼

詳見 git

資源

設計稿藍湖預覽

lanhuapp.com/url/lYuz1 密碼: gSKl

藍湖現在收費了,所以檢視標記還請自己上傳 xd 設計稿 商業設計稿檔案不好直接分享, 可以加微信聯絡 ducafecat


© 貓哥

ducafecat.tech

相關文章