window.pipedriveLeadboosterConfig = { basis: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', versie: 2, } ;(functie () { var w = venster als (w.LeadBooster) { console.warn('LeadBooster bestaat al') } anders { w.LeadBooster = { q: [], on: functie (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: functie (n) { this.q.push({ t: 't', n: n }) }, } } })() Code optimaliseren met Query-objecten - The Codest
The Codest
  • Over ons
  • Diensten
    • Software Ontwikkeling
      • Frontend ontwikkeling
      • Backend ontwikkeling
    • Staff Augmentation
      • Frontend ontwikkelaars
      • Backend ontwikkelaars
      • Gegevensingenieurs
      • Cloud Ingenieurs
      • QA ingenieurs
      • Andere
    • Het advies
      • Audit & Consulting
  • Industrie
    • Fintech & Bankieren
    • E-commerce
    • Adtech
    • Gezondheidstechnologie
    • Productie
    • Logistiek
    • Automotive
    • IOT
  • Waarde voor
    • CEO
    • CTO
    • Leveringsmanager
  • Ons team
  • Case Studies
  • Weten hoe
    • Blog
    • Ontmoetingen
    • Webinars
    • Bronnen
Carrière Neem contact op
  • Over ons
  • Diensten
    • Software Ontwikkeling
      • Frontend ontwikkeling
      • Backend ontwikkeling
    • Staff Augmentation
      • Frontend ontwikkelaars
      • Backend ontwikkelaars
      • Gegevensingenieurs
      • Cloud Ingenieurs
      • QA ingenieurs
      • Andere
    • Het advies
      • Audit & Consulting
  • Waarde voor
    • CEO
    • CTO
    • Leveringsmanager
  • Ons team
  • Case Studies
  • Weten hoe
    • Blog
    • Ontmoetingen
    • Webinars
    • Bronnen
Carrière Neem contact op
Pijl terug KEREN TERUG
2019-03-08
Software Ontwikkeling

Code optimaliseren met Query-objecten

The Codest

Tomasz Szkaradek

Ontwikkelingsarchitect

Het is heel waarschijnlijk dat je op je werk al heel vaak te maken hebt gehad met overbelaste modellen en een enorm aantal aanroepen in de controllers. Op basis van de kennis in de Rails-omgeving ga ik in dit artikel een eenvoudige oplossing voor dit probleem voorstellen.

Een heel belangrijk aspect van een Rails-applicatie is het minimaliseren van het aantal overbodige afhankelijkheden. Daarom promoot de hele Rails-omgeving sinds kort de service object-benadering en het gebruik van de PORO-methode (Pure Old Ruby Object). Een beschrijving van het gebruik van een dergelijke oplossing vind je hier hier. In dit artikel zullen we het concept stap voor stap oplossen en aanpassen aan het probleem.

Probleem

In een hypothetische toepassing hebben we te maken met een ingewikkeld transactiesysteem. Ons model, dat elke transactie voorstelt, heeft een set scopes die je helpen om gegevens te krijgen. Dit vergemakkelijkt het werk enorm, omdat het op één plek te vinden is. Dit duurt echter niet lang. Met de ontwikkeling van de applicatie worden de project wordt steeds ingewikkelder. Scopes hebben niet langer eenvoudige 'waar'-verwijzingen, we missen gegevens en beginnen relaties te laden. Na een tijdje doet het denken aan een ingewikkeld systeem van spiegels. En, wat nog erger is, we weten niet hoe we een lambda met meerdere regels moeten maken!

Hieronder vind je een al uitgebreid toepassingsmodel. De transacties van het betalingssysteem worden hierin opgeslagen. Zoals je in het onderstaande voorbeeld kunt zien:

Klasse Transactie  { where(visible: true) }

  scope(:active, lambda do
    joins(<<-SQL
      LEFT OUTER JOIN bron OP transacties.bron_id = bron.id
      AND source.accepted_at IS NOT NULL
    SQL
  einde)
einde

Het model is één ding, maar als de schaal van ons project toeneemt, beginnen ook de controllers te zwellen. Laten we eens kijken naar het voorbeeld hieronder:

klasse TransactiesController < ApplicationController
  def index
    @transacties = Transactie.voor_uitgevers
                                   .actief
                                   .zichtbaar
                                   .joins("LEFT JOIN withdrawal_items ON withdrawal_items.transaction_id = transactions.id")
                                   .joins("LEFT JOIN withdrawals ON withdrawals.id = withdrawal_items.withdrawal_id OR
 (opnames.id = bron.resource_id AND bron.resource_type = 'Opname')")
                                   .order(:created_at)
                                   .page(params[:pagina])
                                   .per(params[:pagina])
    @transacties = apply_filters(@transacties)
  einde
einde

Hier zien we veel regels van geketende methoden naast extra joins die we niet op veel plaatsen willen uitvoeren, alleen op deze specifieke plaats. De bijgevoegde gegevens worden later gebruikt door de methode apply_filters, die de juiste gegevensfiltering toevoegt, gebaseerd op de GET-parameters. Natuurlijk kunnen we een aantal van deze verwijzingen overbrengen naar de scope, maar is dit niet het probleem dat we eigenlijk proberen op te lossen?

Oplossing

Omdat we al weten dat we een probleem hebben, moeten we dit oplossen. Op basis van de verwijzing in de inleiding gebruiken we hier de PORO-benadering. In dit exacte geval wordt deze aanpak het query object genoemd, wat een ontwikkeling is van het service objecten concept.

Laten we een nieuwe map aanmaken met de naam "services" in de map apps van ons project. Daar maken we een klasse met de naam TransactiesQuery.

klasse TransactiesQuery
einde

Als volgende stap moeten we een initializer maken waarin een standaard aanroeppad voor ons object wordt gemaakt

klasse TransactiesQuery
  def initialiseer(scope = Transaction.all)
    @scope = scope
  einde
einde

Hierdoor kunnen we de relatie van het actieve record overbrengen naar onze faciliteit. Nu kunnen we al onze scopes overbrengen naar de klasse, die alleen nodig zijn in de gepresenteerde controller.

klasse TransactiesQuery
  def initialiseer(scope = Transaction.all)
    @scope = scope
  einde

  privé

  def active(scope)
    scope.joins(<<-SQL
      LEFT OUTER JOIN bron OP transacties.bron_id = bron.id
      AND source.accepted_at IS NOT NULL
    SQL
  einde

  def zichtbaar(scope)
    scope.where(visible: true)
  einde

  def for_publishers(scope)
    bereik.select("transacties.*")
         .joins(:account)
         .where("accounts.owner_type = 'Publisher'")
         .joins("JOIN uitgevers OP eigenaar_id = uitgevers.id")
  einde
einde

We missen nog steeds het belangrijkste deel, namelijk het verzamelen van gegevens in één string en het openbaar maken van de interface. De methode waarin we alles samenvoegen, wordt een "call" genoemd.

Wat echt belangrijk is, is dat we daar de instantievariabele @scope gebruiken, waar het bereik van onze aanroep zich bevindt.

klasse TransactiesQuery
  ...
  aanroepen
    zichtbaar(@scope)
        .then(&method(:active))
        .then(&methode(:voor_uitgevers))
        .order(:created_at)
  einde

  privé
  ...
einde

De hele klas presenteert zich als volgt:

klasse TransactiesQuery
  def initialiseer(scope = Transaction.all)
    @scope = scope
  einde

  def aanroep
    zichtbaar(@scope)
        .then(&methode(:active))
        .then(&methode(:voor_uitgevers))
        .order(:created_at)
  einde

  privé

  def active(scope)
    scope.joins(<<-SQL
      LEFT OUTER JOIN bron OP transacties.bron_id = bron.id
      AND source.accepted_at IS NOT NULL
    SQL
  einde

  def zichtbaar(scope)
    scope.where(visible: true)
  einde

  def for_publishers(scope)
    bereik.select("transacties.*")
         .joins(:account)
         .where("accounts.owner_type = 'Publisher'")
         .joins("JOIN uitgevers OP eigenaar_id = uitgevers.id")
  einde
einde

Na het opschonen ziet het model er duidelijk lichter uit. Daar richten we ons alleen op de gegevensvalidatie en relaties tussen andere modellen.

Klasse Transactie < ActiveRecord::Base
  behoort_tot :rekening
  heeft_één :opname_item
einde

De controller heeft onze oplossing al geïmplementeerd; we hebben alle aanvullende query's naar een aparte klasse verplaatst. De aanroepen die we niet in het model hadden, blijven echter een onopgelost probleem. Na enkele wijzigingen ziet onze indexactie er als volgt uit:

klasse TransactiesController < ApplicationController
  def index
    @transacties = TransactiesQuery.new
                                     .aanroepen
                                     .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
 (opnames.id = bron.resource_id AND bron.resource_type = 'Opname')")
                                     .order(:created_at)
                                     .page(params[:pagina])
                                     .per(params[:pagina])
    @transacties = apply_filters(@transacties)
  einde
einde

Oplossing

In het geval van het implementeren van goede praktijken en conventies, kan het een goed idee zijn om alle gelijksoortige voorkomens van een bepaald probleem te vervangen. Daarom verplaatsen we de SQL query van de indexactie naar het aparte queryobject. We noemen dit een TransactiesFilterbareQuery les. De stijl waarin we de les voorbereiden zal vergelijkbaar zijn met die in TransactiesQuery. Als onderdeel van de code veranderingen, zal een meer intuïtieve registratie van grote SQL-queries worden gesmokkeld, met behulp van meerregelige tekenreeksen genaamd heredoc. De beschikbare oplossing vind je hieronder:

klasse TransactiesFilterbareQuery
  def initialiseer(scope = Transaction.all)
    @scope = scope
  einde

  def aanroep
    terugtrekking(@scope).dan(&methode(:terugtrekking_items))
  einde

  privé

  def terugtrekking(scope)
    scope.joins(<<-SQL
      LEFT JOIN onttrekkingen OP onttrekkingen.id = onttrekking_items.onttrekking_id OR
      (intrekkingen.id = bron.resource_id AND bron.resource_type = 'Intrekking')
    SQL
  einde

  def opname_items(scope)
    scope.joins(<<-SQL
      LEFT JOIN opname_items OP opname_items.accounting_event_id = transacties.id
    SQL
  einde
einde

Bij wijzigingen in de controller verminderen we de massa regels door het query-object toe te voegen. Het is belangrijk dat we alles scheiden, behalve het deel dat verantwoordelijk is voor de paginering.

klasse TransactiesController < ApplicationController
  def index
    @transacties = TransactiesQuery.new.call.then do |scope|
      TransactionsFilterableQuery.new(scope).call
    end.page(params[:page]).per(params[:page])

    @transacties = toepassen_filters(@transacties)
  einde
einde

Samenvatting

Query object verandert veel in de benadering van het schrijven van SQL-queries. In ActiveRecord is het heel eenvoudig om alle bedrijfs- en databaselogica in het model te plaatsen, omdat alles op één plek staat. Dit werkt heel goed voor kleinere toepassingen. Naarmate het project complexer wordt, plaatsen we de logica op andere plaatsen. Met hetzelfde query-object kun je query's van leden groeperen in een specifiek probleem.

Dankzij dit hebben we een gemakkelijke mogelijkheid om de code later te laten overerven en vanwege duck typing kun je deze oplossingen ook gebruiken in andere modellen. Het nadeel van deze oplossing is een grotere hoeveelheid code en versnippering van verantwoordelijkheid. Maar of we een dergelijke uitdaging willen aangaan of niet, hangt af van onszelf en hoe erg we ons storen aan vette modellen.

Verwante artikelen

Software Ontwikkeling

Bouw Toekomstbestendige Web Apps: Inzichten van The Codest's Expert Team

Ontdek hoe The Codest uitblinkt in het creëren van schaalbare, interactieve webapplicaties met geavanceerde technologieën, het leveren van naadloze gebruikerservaringen op alle platforms. Ontdek hoe onze expertise digitale transformatie en business...

DE BESTE
Software Ontwikkeling

Top 10 in Letland gevestigde bedrijven voor softwareontwikkeling

Lees meer over de beste softwareontwikkelingsbedrijven van Letland en hun innovatieve oplossingen in ons nieuwste artikel. Ontdek hoe deze technologieleiders uw bedrijf kunnen helpen verbeteren.

thecodest
Oplossingen voor ondernemingen en schaalvergroting

Essentiële Java-softwareontwikkeling: Een gids voor succesvol uitbesteden

Verken deze essentiële gids over succesvolle outsourcing Java-softwareontwikkeling om de efficiëntie te verbeteren, toegang te krijgen tot expertise en projectsucces te stimuleren met The Codest.

thecodest
Software Ontwikkeling

De ultieme gids voor outsourcing in Polen

De sterke groei van outsourcing in Polen wordt gedreven door economische, educatieve en technologische vooruitgang, die IT-groei en een bedrijfsvriendelijk klimaat stimuleert.

DeCodest
Oplossingen voor ondernemingen en schaalvergroting

De complete gids voor IT-auditmiddelen en -technieken

IT-audits zorgen voor veilige, efficiënte en compliant systemen. Lees het volledige artikel om meer te weten te komen over het belang ervan.

The Codest
Jakub Jakubowicz CTO & medeoprichter

Abonneer je op onze kennisbank en blijf op de hoogte van de expertise uit de IT-sector.

    Over ons

    The Codest - Internationaal softwareontwikkelingsbedrijf met technische hubs in Polen.

    Verenigd Koninkrijk - Hoofdkantoor

    • Kantoor 303B, 182-184 High Street North E6 2JA
      Londen, Engeland

    Polen - Lokale technologieknooppunten

    • Fabryczna kantorenpark, Aleja
      Pokoju 18, 31-564 Krakau
    • Hersenambassade, Konstruktorska
      11, 02-673 Warschau, Polen

      The Codest

    • Home
    • Over ons
    • Diensten
    • Case Studies
    • Weten hoe
    • Carrière
    • Woordenboek

      Diensten

    • Het advies
    • Software Ontwikkeling
    • Backend ontwikkeling
    • Frontend ontwikkeling
    • Staff Augmentation
    • Backend ontwikkelaars
    • Cloud Ingenieurs
    • Gegevensingenieurs
    • Andere
    • QA ingenieurs

      Bronnen

    • Feiten en fabels over samenwerken met een externe partner voor softwareontwikkeling
    • Van de VS naar Europa: Waarom Amerikaanse startups besluiten naar Europa te verhuizen
    • Tech Offshore Ontwikkelingshubs Vergelijking: Tech Offshore Europa (Polen), ASEAN (Filippijnen), Eurazië (Turkije)
    • Wat zijn de grootste uitdagingen voor CTO's en CIO's?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Gebruiksvoorwaarden website

    Copyright © 2025 door The Codest. Alle rechten voorbehouden.

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