7 Replies Latest reply on Aug 31, 2007 10:58 AM by TomD

    CFCs -- using variables from other CFC's in queries

    TomD
      I am working on a click tracking program for emailings, well I want to exclude our IP address, and the clients IP address to show up in the click details, and I want to omit them from being counted when the number of hits are displayed, these are shown on 2 different pages, here is the code I am working with

      Filter.cfc this component takes care of all the filtering of the IPs, and because of the problems I was having w/ the variable not working, I went a head and put all the functions to display the details which shows the ip addy, referrer, and user agent for each click

      [code]
      <!--- -THis method takes out the IPs in the list that are in the Filtered table --->
      <cffunction name="noShowFilterList" returntype="any" access="remote">
      <cfset filterList = "69.15.59.174">
      <cfoutput query="getFilterIPInfo">
      <cfset filterList = filterList & "," & getFilterIPInfo.ipAddress>
      </cfoutput>
      </cffunction>
      [/code]

      [code]
      <!--- This method sets everything up for the table that displays the click information --->
      <cffunction name="getFilteredLinkDetail" access="remote" returntype="any">
      <cfparam name="PageNum_queryDetails" default="1">
      <cfquery name="queryDetails" datasource="clickTrackSystem">
      SELECT *
      FROM Clicks
      WHERE linkID = #url.l# AND NOT (FIND_IN_SET(Clicks.ipAddy, "#filterList#"))
      </cfquery>...
      [/code]

      here is clickDetail.cfm...just the calling of this functionality

      [code]
      <!--- Create a Filter Object --->
      <cfset newFilter=CreateObject('component', 'Filter')>
      <!--- Query all Filtered IPs --->
      <cfinvoke component="#newFilter#" method="displayFilteredIPs" returnvariable="getFilterIPInfo">
      <cfinvoke component="#newFilter#" method="noShowFilterList" returnvariable="filterList">

      [/code]

      Links.cfc (this is just a query in the fucntion that displays the projects links, thier name, target, track link, and the hits, this is the query for the hits, and it works to exclude the detail. The fuction is called linkInfo)
      [code]
      <cfquery name="countHits" datasource="clickTrackSystem">
      SELECT Clicks.linkID
      FROM Clicks
      WHERE Clicks.linkID = #getLinkInfo.linkID# AND NOT (FIND_IN_SET(Clicks.ipAddy, "#filterList#"
      )) </cfquery>
      Hits:#countHits.recordcount#<br>
      [/code]

      And here is me calling all this in Campaign.cfm, where all the links for the project are displayed

      [code]
      <!--- Create an Object of type Links from Links.cfc--->
      <cfset newLink = CreateObject('component', 'Links')>
      <!--- Query the link info--->
      <cfinvoke component="#newLink#" method="displayLinks" returnvariable="getLinkInfo">
      <!--- Create a Filter Object --->
      <cfset newFilter=CreateObject('component', 'Filter')>
      <!--- Query all Filtered IPs --->
      <cfinvoke component="#newFilter#" method="displayFilteredIPs" returnvariable="getFilterIPInfo">
      <cfinvoke component="#newFilter#" method="noShowFilterList" returnvariable="filterList">
      [/code]

      So what is going on, filterList is a variable that holds a comma delimited list of the IPs we don't want to show or count, it is created in the filter component and called in a query in a function in the Links component, everything works where I just use it from the filter component, but when I call it in the link component with in the query, it throws an undefined error:

      Variable filterList is undefined.


      The error occurred in /Links.cfc: line 57

      55 : SELECT Clicks.linkID
      56 : FROM Clicks
      57 : WHERE Clicks.linkID = #getLinkInfo.linkID# AND NOT (FIND_IN_SET(Clicks.ipAddy, "#filterList#"))
      58 : </cfquery>
      59 : Hits:#countHits.recordcount#<br>

      THis is my first program ever outside of school, and I learned about OOP with JAVA in school, but never really got to apply it, I know there is some rule I am breaking or something for CFC's and maybe even the OOP concept all together...even if I put that query out of the Links component and hardcode it in the Campaign.cfm page, I still get the error, but it works for the clickDetail page and I know it is because all the fuctions it needs to do so are all done in the same cfc: Filter. If I can't get this to work, then I will have to hard code all this in two different pages, and that defeats the purpose of using cfc's, eventually we want to make a whole mass emailing system and just have the clicktracking as a feature, so cfcs are going to be important...someone please make me understand where I am going wrong

      Kacie
        • 1. Re: CFCs -- using variables from other CFC's in queries
          Level 7
          > THis is my first program ever outside of school, and I learned about OOP with
          > JAVA in school, but never really got to apply it, I know there is some rule I
          > am breaking or something for CFC's and maybe even the OOP concept all
          > together...

          Right. First things first then: you might need to brush up a bit on what
          you're trying to achieve. Given yourself a refreshed in OO concepts.
          Here's one link:
          http://en.wikipedia.org/wiki/Object-oriented_programming

          And Google will have plenty more, if the Wikipedia article is a bit dry (be
          warned: almost all coverage of OO concepts will be a bit dry!)

          Next, read the docs on CFCs that Adobe very kindly expose to the public,
          here:
          http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=buildingComponents_01.ht ml

          Specifically, read and understand the coverage of the different variables
          scopes available to CFC instances, and how to use them:
          http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=buildingComponents_01.ht ml


          > <cffunction name="noShowFilterList" returntype="any" access="remote">
          > <cfset filterList = "69.15.59.174">

          You should be VARing this variable. And all variables local to a method.
          If it IS actually your intent for the variable to be global to the CFC
          instance, SCOPE IT as variables.filterList. CF will work out what you mean
          without the scoping, but you can make the life easier of humans reading the
          code if you scope your variables as much as possible, whether they "need"
          it or not.


          > <cfoutput query="getFilterIPInfo">

          Where did this query come from? You didn't pass it in to the method. Is
          it an argument? (You should have a <cfargument> tag for it, and you should
          scope it. Is it a instance-wide variables (variables scope)? Scope it for
          clarity. Similarly if it is THIS-scoped.


          > <cfset filterList = filterList & "," & getFilterIPInfo.ipAddress>
          > </cfoutput>

          You don't need to have this loop. Just use valueList().


          > <cffunction name="getFilteredLinkDetail" access="remote" returntype="any">
          > <cfparam name="PageNum_queryDetails" default="1">

          I suspect this should be a VAR statement.


          > <cfquery name="queryDetails" datasource="clickTrackSystem">

          This should be VARed.


          > SELECT *

          Using SELECT * is generally poor practice. Do you actually WANT every
          column from the query, irrespective of what those columns are? I suspect
          not. SELECT only the columns you intend to use; it's much less work for
          the DB server and the CF server.


          > WHERE linkID = #url.l# AND NOT (FIND_IN_SET(Clicks.ipAddy, "#filterList#"))

          In referring to a URL-scoped variable directly in your CFC code, you are
          limiting the reusability of the CFC. This value should be passed in.

          And you should NEVER EVER use a URL-scope variable directly in a query
          anyhow. Google "SQL injection".

          Always use <cfqueryparam> tags in your queries, when using dynamic values.
          Otherwise all your SQL will be treated as dynamic SQL by the DB server, and
          you'll end up with 100s of individually compiled and never-reused queries
          sitting on your DB server. Initial compilation has quite an overhead which
          you'll want to minimise. Parameterising queries also prevents the chance
          of SQL injection.


          > <cfset newFilter=CreateObject('component', 'Filter')>
          > <!--- Query all Filtered IPs --->
          > <cfinvoke component="#newFilter#" method="displayFilteredIPs"
          > returnvariable="getFilterIPInfo">

          Right. So this is where that query comes from. However...


          > <cfinvoke component="#newFilter#" method="noShowFilterList"
          > returnvariable="filterList">

          ... you're never passing it in to the method call, are you? So how's the
          method supposed to know about it? Code in CFCs know ONLY about their own
          VARIABLES scope; they have no idea about the calling-code's VARIABLES
          scope. You need to pass in calling-code variables that you wish the CFC
          method to have access to.


          > <cfinvoke component="#newLink#" method="displayLinks"
          > returnvariable="getLinkInfo">

          Can I recommend that instead of using all these clunky <cfinvoke> tags, you
          simply:

          <cfset getLinkInfo = newLink.displayLinks()>

          It's much cleaner.

          <cfinvoke> is a bit of a lemon of a tag, for most of its "recommended" (in
          the docs, that is) uses.


          > Variable filterList is undefined.
          > The error occurred in /Links.cfc: line 57
          >
          > 55 : SELECT Clicks.linkID
          > 56 : FROM Clicks
          > 57 : WHERE Clicks.linkID = #getLinkInfo.linkID# AND NOT

          I hope you understand why this is, now?

          --
          Adam
          • 2. Re: CFCs -- using variables from other CFC's in queries
            TomD Level 1
            Thank you Adam, I will get to work on this right now, and let you know how it goes...

            "> <cfoutput query="getFilterIPInfo">

            Where did this query come from? You didn't pass it in to the method. Is
            it an argument? (You should have a <cfargument> tag for it, and you should
            scope it. Is it a instance-wide variables (variables scope)? Scope it for
            clarity. Similarly if it is THIS-scoped."

            The query getFilterInfo, is in another method in the same CFC

            'THIS' is something I just don't understand, I can never find any OO examples where there is interaction with databases.

            I will take a look at those websites you gave me and begin trying to work all this out, you should be hearing more from me later on in the day...

            Thanks
            • 3. Re: CFCs -- using variables from other CFC's in queries
              TomD Level 1
              "... you're never passing it in to the method call, are you? So how's the
              method supposed to know about it? Code in CFCs know ONLY about their own
              VARIABLES scope; they have no idea about the calling-code's VARIABLES
              scope. You need to pass in calling-code variables that you wish the CFC
              method to have access to."

              I understand what you are saying, but I don't know how. I thought since the cfm page had filterlist as a return variable then the page would pass that variable to the cfc when the method was called. Obiviously I was wrong, I even tried, knowing it was not right, to create and object of Filter and invoke that method with the query in the Links cfc, and of course I was right it didn't work. That was basically my question...how do I get Links.cfc to know what filterlist is? Note I will take your better practices suggestions once I get this all figured out, I put variables.filterList and it just threw and error that filterList was undefined in variables, and that threw me off, so once I get this going I will go back and fix it, but I am confused enough for now

              Here is what I have tried:

              a new fucntion in Links.cfc
              <cffunction name="passFilterList">
              <cfargument name="filterList" required="no" type="string">
              <cfset this.filterList = arguments.filterList>
              </cffunction>

              the query in the function that does the output
              ...
              <cfquery name="countHits" datasource="clickTrackSystem">
              SELECT Clicks.linkID
              FROM Clicks
              WHERE Clicks.linkID = #getLinkInfo.linkID# AND NOT (FIND_IN_SET(Clicks.ipAddy, "#filterList#"))
              </cfquery>
              ...


              new code for campaign.cfm

              <!--- Create a Filter Object --->
              <cfset newFilter=CreateObject('component', 'Filter')>
              <!--- Query all Filtered IPs --->
              <cfinvoke component="#newFilter#" method="displayFilteredIPs" returnvariable="getFilterIPInfo">
              <cfinvoke component="#newFilter#" method="noShowFilterList" returnvariable="filterList">
              <cfdump var = "#filterList#"> <!--- Its there! --->
              <!--- Create an Object of type Links from Links.cfc--->
              <cfset newLink = CreateObject('component', 'Links')>
              <!--- Query the link info--->
              <cfset newLink.passFilterList('#filterList#')>
              <cfinvoke component="#newLink#" method="displayLinks" returnvariable="getLinkInfo">
              .....
              I even pass it down here
              <!--- Display Link information--->
              <cfset newLink.linkInfo('#filterList#')>



              Still filterList is undefined, I even tried putting filterList into another variable and passing it using the new variable, but no, no no I can't get Links.cfc to comply
              btw I need cfinvoke when my functions have queries in them....right?

              "> <cffunction name="getFilteredLinkDetail" access="remote" returntype="any">
              > <cfparam name="PageNum_queryDetails" default="1">

              I suspect this should be a VAR statement."

              funny you mention that because I get an error for that as well, I use it in the table in the page, and it is undefined, I changed it like this:
              <cfset VAR PageNum_queryDetails = "1">
              and still undefined, is that not what you meant?

              "> <cfquery name="queryDetails" datasource="clickTrackSystem">

              This should be VARed.
              "
              How do you VAR a query?

              "> <cfset filterList = filterList & "," & getFilterIPInfo.ipAddress>
              > </cfoutput>

              You don't need to have this loop. Just use valueList().
              "

              I found syntax for this and I understand it, but how can I tell it to start out with a value and add the list to that value the way we have it set up now? We need to put our IP address in the list first and then tack on what it finds in the database, if we put it in the Database, we will have to put it in there for every client.

              Thanks in advance for you patience and help
              • 4. Re: CFCs -- using variables from other CFC's in queries
                Level 7
                > Specifically, read and understand the coverage of the different variables
                > scopes available to CFC instances, and how to use them:
                > http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=buildingComponents_01.ht ml

                Sorry, that should read
                http://livedocs.adobe.com/coldfusion/8/htmldocs/buildingComponents_29.html.

                --
                Adam
                • 5. Re: CFCs -- using variables from other CFC's in queries
                  Level 7
                  > I understand what you are saying, but I don't know how.

                  Right.

                  Did you read all that bumpf I gave you the link for, before (about CFCs)?
                  That was an awful lot of it to:
                  a) read over;
                  b) investigate and experiment with.

                  I didn't mean just the single pages I referenced, I meant the ensuing
                  section (there's about 30-odd pages in it). There are next/prev arrows @
                  the top right of each page.

                  I recommend you ditch your immediate requirement, and work on the examples
                  given in the docs for a bit until you get your head around them. And then
                  return to your specific problem once you've done that.


                  > cfm page had filterlist as a return variable then the page would pass that
                  > variable to the cfc when the method was called.

                  Only if you tell it to. You need to pass in these values into CFC methods
                  just like you would any other function.

                  You'd not have code like this:

                  <cfset stringToTrim = "Hello World">
                  <cfset charactersWanted = 5>
                  <cfset newString = left()>

                  And expect newString to contain "Hello". No, you need to pass the values
                  to the function:

                  <cfset newString = left(stringToTrim, charactersWanted)>

                  CFC methods are the same. They're just functions. For them to receive a
                  value, you have to pass them in.

                  This IS all covered in the docs I pointed you to.


                  > Note I will take your better practices suggestions once I get
                  > this all figured out,

                  Makes sense. Like I suggested: I'd back off even further and try the more
                  simple examples in the docs first.


                  > funny you mention that because I get an error for that as well, I use it in
                  > the table in the page, and it is undefined, I changed it like this:
                  > <cfset VAR PageNum_queryDetails = "1">
                  > and still undefined, is that not what you meant?

                  Just as a practice, one should always VAR variables used in functions. I
                  was not meaning to suggest it'd fix your issue, just something you should
                  be doing.


                  >>> <cfquery name="queryDetails" datasource="clickTrackSystem">
                  >> This should be VARed.

                  > How do you VAR a query?

                  A query is just a variable, like any other variable. So one vars it in the
                  same way.

                  <cfset var queryDetails = ""><!--- any old value is fine --->
                  ...
                  <cfquery name="queryDetails" datasource="clickTrackSystem">


                  >>> <cfset filterList = filterList & "," & getFilterIPInfo.ipAddress>
                  >> You don't need to have this loop. Just use valueList().

                  > I found syntax for this and I understand it, but how can I tell it to start
                  > out with a value and add the list to that value the way we have it set up now?

                  Right. You have a string (filterList) and you want to preprend it to a
                  list (the result of your valueList() call).

                  You could either treat your valueList() as a string and prepend filterList
                  in exactly the same way as you are above (just without the loop), or you
                  could approach it from the "list" perspective and investigate which list
                  functions might help you there.

                  http://livedocs.adobe.com/coldfusion/8/htmldocs/functions-pt0_13.html#1099435


                  > Thanks in advance for you patience and help

                  No prob. Sorry to not just be answering your questions directly (ie: "the
                  answer to your problem is [this]"), but I don't believe that approach is
                  very helpful, in the long run. I think it's best to learn what the answer
                  is, rather than to be told it.

                  --
                  Adam
                  • 6. Re: CFCs -- using variables from other CFC's in queries
                    TomD Level 1
                    you are absolutely right, I will be studying on this for the rest of the day and more tomorrow, and post sometime tomorrow afternoon

                    Thanks
                    • 7. Re: CFCs -- using variables from other CFC's in queries
                      TomD Level 1
                      I got it ironed out using the variables scope, I still need to wrap my head around some stuff but I am just glad it works, thanks for the help!