window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(funktion () { var w = vindue if (w.LeadBooster) { console.warn('LeadBooster findes allerede') } else { w.LeadBooster = { q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: function (n) { this.q.push({ t: 't', n: n }) }, } } })() Optimering af kode med Query Objects - The Codest
Codest
  • Om os
  • Serviceydelser
    • Udvikling af software
      • Frontend-udvikling
      • Backend-udvikling
    • Staff Augmentation
      • Frontend-udviklere
      • Backend-udviklere
      • Dataingeniører
      • Cloud-ingeniører
      • QA-ingeniører
      • Andet
    • Det rådgivende
      • Revision og rådgivning
  • Industrier
    • Fintech og bankvirksomhed
    • E-commerce
    • Adtech
    • Sundhedsteknologi
    • Produktion
    • Logistik
    • Biler
    • IOT
  • Værdi for
    • ADMINISTRERENDE DIREKTØR
    • CTO
    • Leder af levering
  • Vores team
  • Casestudier
  • Ved hvordan
    • Blog
    • Møder
    • Webinarer
    • Ressourcer
Karriere Tag kontakt til os
  • Om os
  • Serviceydelser
    • Udvikling af software
      • Frontend-udvikling
      • Backend-udvikling
    • Staff Augmentation
      • Frontend-udviklere
      • Backend-udviklere
      • Dataingeniører
      • Cloud-ingeniører
      • QA-ingeniører
      • Andet
    • Det rådgivende
      • Revision og rådgivning
  • Værdi for
    • ADMINISTRERENDE DIREKTØR
    • CTO
    • Leder af levering
  • Vores team
  • Casestudier
  • Ved hvordan
    • Blog
    • Møder
    • Webinarer
    • Ressourcer
Karriere Tag kontakt til os
Pil tilbage GÅ TILBAGE
2019-03-08
Udvikling af software

Optimering af kode med Query Objects

Codest

Tomasz Szkaradek

Udviklingsarkitekt

Det er meget sandsynligt, at du på dit arbejde er stødt på overbelastede modeller og et stort antal kald i controllerne en hel del gange. Baseret på viden om Rails-miljøet vil jeg i denne artikel foreslå en enkel løsning på dette problem.

Et meget vigtigt aspekt af Rails-applikationen er at minimere antallet af overflødige afhængigheder, hvilket er grunden til, at hele Rails-miljøet for nylig har fremmet serviceobjekttilgangen og brugen af PORO-metoden (Pure Old Ruby Object). En beskrivelse af, hvordan man bruger en sådan løsning, finder du her her. I denne artikel vil vi løse konceptet trin for trin og tilpasse det til problemet.

Problem

I en hypotetisk applikation har vi at gøre med et kompliceret transaktionssystem. Vores model, som repræsenterer hver transaktion, har et sæt scopes, som hjælper dig med at hente data. Det er en stor arbejdslettelse, da det kan findes ét sted. Men det varer ikke længe. Med udviklingen af applikationen bliver projekt bliver mere og mere kompliceret. Scopes har ikke længere simple 'where'-referencer, vi mangler data og begynder at indlæse relationer. Efter et stykke tid minder det om et kompliceret system af spejle. Og hvad værre er, vi ved ikke, hvordan man laver en lambda med flere linjer!

Nedenfor finder du en allerede udvidet applikationsmodel. Betalingssystemets transaktioner er gemt i. Som du kan se i eksemplet nedenfor:

class Transaction  { where(visible: true) }

  scope(:active, lambda do
    joins(<<-SQL
      LEFT OUTER JOIN source ON transactions.source_id = source.id
      AND source.accepted_at IS NOT NULL
    SQL
  end)
slut

Modellen er én ting, men når omfanget af vores projekt stiger, begynder controllerne også at svulme op. Lad os se på eksemplet nedenfor:

class TransactionsController < ApplikationsController
  def index
    @transactions = Transaction.for_publishers
                                   .active
                                   .visible
                                   .joins("LEFT JOIN withdrawal_items ON withdrawal_items.transaction_id = transactions.id")
                                   .joins("LEFT JOIN withdrawals ON withdrawals.id = withdrawal_items.withdrawal_id OR
 (withdrawals.id = source.resource_id AND source.resource_type = 'Withdrawal')")
                                   .order(:created_at)
                                   .page(params[:page])
                                   .per(params[:page])
    @transactions = apply_filters(@transactions)
  slut
slut

Her kan vi se mange linjer med kædede metoder sammen med yderligere sammenføjninger, som vi ikke ønsker at udføre mange steder, men kun i netop denne. De vedhæftede data bruges senere af metoden apply_filters, som tilføjer den passende datafiltrering baseret på GET-parametrene. Vi kan selvfølgelig overføre nogle af disse referencer til scope, men er det ikke det problem, vi faktisk forsøger at løse?

Løsning

Da vi allerede ved, at vi har et problem, skal vi løse det. Baseret på referencen i indledningen vil vi bruge PORO-tilgangen her. I dette tilfælde kaldes denne tilgang for forespørgselsobjektet, som er en udvikling af serviceobjektkonceptet.

Lad os oprette en ny mappe med navnet "services", som ligger i vores projekts app-mappe. Der vil vi oprette en klasse ved navn TransactionsQuery.

klasse TransactionsQuery
slutning

Som et næste skridt skal vi oprette en initializer, hvor der oprettes en standardopkaldssti til vores objekt

klasse TransactionsQuery
  def initialize(scope = Transaction.all)
    @scope = område
  slut
end

Takket være dette vil vi være i stand til at overføre relationen fra den aktive post til vores facilitet. Nu kan vi overføre alle vores scopes til klassen, som kun er nødvendige i den præsenterede controller.

klasse TransactionsQuery
  def initialize(scope = Transaction.all)
    @scope = område
  slut

  privat

  def active(scope)
    scope.joins(<<-SQL
      LEFT OUTER JOIN source ON transactions.source_id = source.id
      AND source.accepted_at IS NOT NULL
    SQL
  end

  def visible(scope)
    scope.where(visible: true)
  end

  def for_publishers(scope)
    scope.select("transaktioner.*")
         .joins(:account)
         .where("accounts.owner_type = 'Publisher'")
         .joins("JOIN publishers ON owner_id = publishers.id")
  slut
slut

Vi mangler stadig den vigtigste del, nemlig at samle data i en streng og gøre grænsefladen offentlig. Den metode, hvor vi samler det hele, får navnet "call".

Det, der virkelig er vigtigt, er, at vi bruger @scope-instansvariablen der, hvor omfanget af vores kald er placeret.

klasse TransactionsQuery
  ...
  def opkald
    visible(@scope)
        .then(&method(:active))
        .then(&method(:for_publishers))
        .order(:created_at)
  slut

  privat
  ...
end

Hele klassen præsenterer sig som følger:

klasse TransactionsQuery
  def initialize(scope = Transaction.all)
    @scope = område
  slut

  def kald
    visible(@scope)
        .then(&method(:active))
        .then(&method(:for_publishers))
        .order(:created_at)
  slut

  privat

  def active(scope)
    scope.joins(<<-SQL
      LEFT OUTER JOIN source ON transactions.source_id = source.id
      AND source.accepted_at IS NOT NULL
    SQL
  end

  def visible(scope)
    scope.where(visible: true)
  end

  def for_publishers(scope)
    scope.select("transaktioner.*")
         .joins(:account)
         .where("accounts.owner_type = 'Publisher'")
         .joins("JOIN publishers ON owner_id = publishers.id")
  slut
slut

Efter vores oprydning ser modellen helt klart lettere ud. Der fokuserer vi kun på datavalidering og relationer mellem andre modeller.

class Transaction < ActiveRecord::Base
  hører til :konto
  har_en :udbetalingspost
slut

Controlleren har allerede implementeret vores løsning; vi har flyttet alle yderligere forespørgsler til en separat klasse. Men de kald, vi ikke havde i modellen, er stadig et uløst problem. Efter nogle ændringer ser vores indekshandling sådan ud:

class TransactionsController < ApplikationsController
  def index
    @transactions = TransactionsQuery.new
                                     .call
                                     .joins("LEFT JOIN withdrawal_items ON withdrawal_items.accounting_event_id = transactions.id")
                                     .joins("LEFT JOIN withdrawals ON withdrawals.id = withdrawal_items.withdrawal_id OR
 (withdrawals.id = source.resource_id AND source.resource_type = 'Withdrawal')")
                                     .order(:created_at)
                                     .page(params[:page])
                                     .per(params[:page])
    @transactions = apply_filters(@transactions)
  slut
slut

Løsning

I tilfælde af implementering af god praksis og konventioner kan det være en god idé at erstatte alle lignende forekomster af et givet problem. Derfor flytter vi SQL-forespørgslen fra indekshandlingen til det separate forespørgselsobjekt. Vi vil kalde dette en TransactionsFilterableQuery klasse. Stilen, som vi forbereder klassen i, vil ligne den, der præsenteres i TransactionsQuery. Som en del af Kode ændringer, vil der blive smuglet en mere intuitiv registrering af store SQL-forespørgsler ved hjælp af flerlinjede tegnstrenge kaldet heredoc. Den tilgængelige løsning finder du nedenfor:

klasse TransactionsFilterableQuery
  def initialize(scope = Transaction.all)
    @scope = område
  slut

  def opkald
    withdrawal(@scope).then(&method(:withdrawal_items))
  end

  privat

  def tilbagetrækning(scope)
    scope.joins(<<-SQL
      LEFT JOIN withdrawals ON withdrawals.id = withdrawal_items.withdrawal_id OR
      (withdrawals.id = source.resource_id AND source.resource_type = 'Withdrawal')
    SQL
  slut

  def withdrawal_items(scope)
    scope.joins(<<-SQL
      LEFT JOIN withdrawal_items ON withdrawal_items.accounting_event_id = transactions.id
    SQL
  end
end

I tilfælde af ændringer i controlleren reducerer vi mængden af linjer ved at tilføje query-objektet. Det er vigtigt, at vi adskiller alt undtagen den del, der er ansvarlig for paginering.

class TransactionsController < ApplikationsController
  def index
    @transactions = TransactionsQuery.new.call.then do |scope|
      TransactionsFilterableQuery.new(scope).call
    end.page(params[:page]).per(params[:page])

    @transactions = apply_filters(@transactions)
  end
slut

Sammenfatning

Query object ændrer meget på tilgangen til at skrive SQL-forespørgsler. I ActiveRecord er det meget nemt at placere al forretnings- og databaselogik i modellen, da alt er samlet ét sted. Det fungerer fint til mindre applikationer. Når projektets kompleksitet øges, placerer vi logikken andre steder. Det samme forespørgselsobjekt giver dig mulighed for at gruppere medlemsforespørgsler til et specifikt problem.

Takket være dette har vi en nem mulighed for senere nedarvning af koden, og på grund af duck typing kan du også bruge disse løsninger i andre modeller. Ulempen ved denne løsning er en større mængde kode og fragmentering af ansvaret. Men om vi vil tage sådan en udfordring op eller ej, afhænger af os selv og af, hvor meget vi bliver forstyrret af fede modeller.

Relaterede artikler

Udvikling af software

Byg fremtidssikrede webapps: Indsigt fra The Codest's ekspertteam

Oplev, hvordan The Codest udmærker sig ved at skabe skalerbare, interaktive webapplikationer med banebrydende teknologier, der leverer sømløse brugeroplevelser på tværs af alle platforme. Lær, hvordan vores ekspertise driver digital transformation og...

DENKODEST
Udvikling af software

Top 10 Letlands-baserede softwareudviklingsvirksomheder

Læs om Letlands bedste softwareudviklingsvirksomheder og deres innovative løsninger i vores seneste artikel. Find ud af, hvordan disse teknologiledere kan hjælpe med at løfte din virksomhed.

thecodest
Løsninger til virksomheder og scaleups

Grundlæggende om Java-softwareudvikling: En guide til succesfuld outsourcing

Udforsk denne vigtige guide til vellykket outsourcing af Java-softwareudvikling for at forbedre effektiviteten, få adgang til ekspertise og skabe projektsucces med The Codest.

thecodest
Udvikling af software

Den ultimative guide til outsourcing i Polen

Den voldsomme stigning i outsourcing i Polen er drevet af økonomiske, uddannelsesmæssige og teknologiske fremskridt, der fremmer it-vækst og et erhvervsvenligt klima.

TheCodest
Løsninger til virksomheder og scaleups

Den komplette guide til IT-revisionsværktøjer og -teknikker

IT-revisioner sikrer sikre, effektive og kompatible systemer. Lær mere om deres betydning ved at læse hele artiklen.

Codest
Jakub Jakubowicz CTO og medstifter

Tilmeld dig vores vidensbase, og hold dig opdateret om ekspertisen fra it-sektoren.

    Om os

    The Codest - International softwareudviklingsvirksomhed med tech-hubs i Polen.

    Storbritannien - Hovedkvarter

    • Kontor 303B, 182-184 High Street North E6 2JA
      London, England

    Polen - Lokale teknologiske knudepunkter

    • Fabryczna Office Park, Aleja
      Pokoju 18, 31-564 Kraków
    • Hjerneambassaden, Konstruktorska
      11, 02-673 Warszawa, Polen

      Codest

    • Hjem
    • Om os
    • Serviceydelser
    • Casestudier
    • Ved hvordan
    • Karriere
    • Ordbog

      Serviceydelser

    • Det rådgivende
    • Udvikling af software
    • Backend-udvikling
    • Frontend-udvikling
    • Staff Augmentation
    • Backend-udviklere
    • Cloud-ingeniører
    • Dataingeniører
    • Andet
    • QA-ingeniører

      Ressourcer

    • Fakta og myter om at samarbejde med en ekstern softwareudviklingspartner
    • Fra USA til Europa: Hvorfor beslutter amerikanske startups sig for at flytte til Europa?
    • Sammenligning af Tech Offshore-udviklingsknudepunkter: Tech Offshore Europa (Polen), ASEAN (Filippinerne), Eurasien (Tyrkiet)
    • Hvad er de største udfordringer for CTO'er og CIO'er?
    • Codest
    • Codest
    • Codest
    • Privacy policy
    • Vilkår for brug af hjemmesiden

    Copyright © 2025 af The Codest. Alle rettigheder forbeholdes.

    da_DKDanish
    en_USEnglish de_DEGerman sv_SESwedish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian jaJapanese ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek da_DKDanish