(ns core (:require [hiccup.core :as h] [honeysql.core :as sql] [honeysql.helpers :refer :all :as sqlh])) ;; If using reagent you don't need to pass into h/html but this is server side ;; reagent also has some helpers to work with react nicer (h/html [:span "bar"]) ;; attributes are added as a map of values ;; styles are also a map (h/html [:span "bar" {:class "class1 class2" :title "my title" :style {:color "red"}}]) ;; you can shorthand to add id's and classes (h/html [:span#id.class1.class2 "bar"]) ;; A more complex example building a nav bar (h/html [:header.bg-black-90.fixed.w-100.ph3.pv3.pv4-ns.ph4-m.ph5-l [:nav.f6.fw6.ttu.tracked [:a.link.dim.white.dib.mr3 {:href "#" :title "Home"} "Home"] [:a.link.dim.white.dib.mr3 {:href "#" :title "About"} "About"] [:a.link.dim.white.dib.mr3 {:href "#" :title "Store"} "Store"] [:a.link.dim.white.dib {:href "#" :title "Contact"} "Contact"]]]) ;; Obviously the main advantage comes from composing, so we can break this into function's (defn navbar-link [{:keys [href title text] :or {text nil title nil}}] [:a.link.dim.white.dib.mr3 {:href href :title title} text]) (defn navbar [links] [:header.bg-black-90.fixed.w-100.ph3.pv3.pv4-ns.ph4-m.ph5-l [:nav.f6.fw6.ttu.tracked (map navbar-link links)]]) ;; now its only a hash map and no html, in reagent you can use atom for live updating ;; notice how nil removes the attribute's so its easy to make something optional (h/html (navbar [{:href "link1" :title "title here"} {:href "link2" :title nil} {:href "link3" :text "link text"} {:href "link4"}])) ;; place parts inside another containing element (h/html (into [:div.container] [[:span "span 1"] [:span "span 2"]])) ;; you could also use merge (h/html (merge [:span "span 1"] [:span "span 2"] [:span "span 3"])) ;; we can take advantage of lazyness if we like (h/html (take 2 (map navbar-link [{:href "link1" :title "title here"} {:href "link2" :title nil} {:href "link3" :text "link text"} {:href "link4"}]))) ;; first tip is sql/format will convert to an sql query, use it to split things up ;; in this example we create a select but with no from format will still give you a query ;; when you run the query you will get an error (sql/format (sqlh/select :first_name :last_name :email)) ;; we can join fn's together to add data ;; in this example we add the from (sql/format (sqlh/from (sqlh/select :first_name :last_name :email) :users)) ;; how ever it's much nicer to use the threading macro (sql/format (-> (sqlh/select :first_name :last_name :email) (sqlh/from :users))) ;; ordering does not matter other than for your sanity :-) (sql/format (-> (sqlh/from :users) (sqlh/select :first_name :last_name :email))) ;; we can do our usual things like limiting ordering etc (sql/format (-> (sqlh/select :first_name :last_name :email) (sqlh/from :users) (sqlh/order-by :first_name) (sqlh/limit 10))) (sql/format (-> (sqlh/select :first_name :last_name :email) (sqlh/from :users) (sqlh/where [:= :first_name "spot"] [:= :last_name "dog"]))) ;; honey gives us a range of merge functions, below base-sql is a simple query ;; user-search extend the query replacing the selected column's and appends the where clauses if values are supplied, we could also use merge-select to extend the previous select. ;; we can use =if= =when= =when-let= =cond->= to help build these. (def base-sql (-> (sqlh/select :first_name :last_name :email) (sqlh/from :users))) (defn user-search [{:keys [first-name last-name] :or {first-name nil last-name nil}}] (sql/format (-> base-sql (sqlh/select :first_name :last_name) (sqlh/merge-where (when first-name [:= :first_name first-name])) (sqlh/merge-where (when last-name [:= :last_name last-name]))))) (sql/format (user-search {:first-name "spot"})) ;; now is a good time to explain aliasing, basically keywords become vectors so :first_name would become [:first_name ] (sql/format (-> base-sql (select [:first_name :fn] [:last_name :ln] [:email :e]))) ;; extending base above we can add in an inner join statement ;; we can also alias the joined table name (def base-join-sql (-> base-sql (sqlh/join [:address :a] [:= :users.address_id :a.id]))) (sql/format base-join-sql) ;; grouping by name count occurrences (def base-group-sql (-> base-sql (sqlh/select :first_name [:%count.first_name :count_name]) (sqlh/group :first_name))) (sql/format base-group-sql) ;; when all else fails you have a few options, break out =sql/raw= or extending honey sql ;; say we want to get people added in the last 14 days this is a bit more tricky (def base-last-14-days-sql (-> base-sql (sqlh/where [:> (sql/raw "created") (sql/raw "CURRENT_DATE - INTERVAL '14' DAY")]))) (sql/format base-last-14-days-sql) (def big-base-sql (-> (sqlh/select :users.* :address.* :products.*) (sqlh/from :users) (sqlh/join :address [:= :users.address_id :address.id]) (sqlh/merge-join :products [:= :users.address_id :address.id]) (sqlh/limit 100))) (defn big-base-filters [filters] (-> big-base-sql (sqlh/merge-where (when (:first_name filters) [:= :first_name (:first_name filters)])) (sqlh/merge-where (when (:last_name filters) [:= :last_name (:last_name filters)])) (sqlh/merge-where (when (:product_name filters) [:= :product.name (:product_name filters)])) (sqlh/merge-where (when (:active filters) [:= :active (:active filters)])))) (sql/format (big-base-filters {:first_name "spot" :last_name "dog" :product_name "lead" :active true}))