1"use strict";(globalThis.webpackChunkrekalogika_docs=globalThis.webpackChunkrekalogika_docs||[]).push([[6050],{1470:(e,r,n)=>{n.d(r,{A:()=>T});var a=n(6540),t=n(4164),s=n(7559),l=n(3104),o=n(6347),i=n(205),c=n(7485),u=n(1682),d=n(679);function h(e){return a.Children.toArray(e).filter(e=>"\n"!==e).map(e=>{if(!e||(0,a.isValidElement)(e)&&function(e){const{props:r}=e;return!!r&&"object"==typeof r&&"value"in r}(e))return e;throw new Error(`Docusaurus error: Bad <Tabs> child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.`)})?.filter(Boolean)??[]}function p(e){const{values:r,children:n}=e;return(0,a.useMemo)(()=>{const e=r??function(e){return h(e).map(({props:{value:e,label:r,attributes:n,default:a}})=>({value:e,label:r,attributes:n,default:a}))}(n);return function(e){const r=(0,u.XI)(e,(e,r)=>e.value===r.value);if(r.length>0)throw new Error(`Docusaurus error: Duplicate values "${r.map(e=>e.value).join(", ")}" found in <Tabs>. Every value needs to be unique.`)}(e),e},[r,n])}function m({value:e,tabValues:r}){return r.some(r=>r.value===e)}function b({queryString:e=!1,groupId:r}){const n=(0,o.W6)(),t=function({queryString:e=!1,groupId:r}){if("string"==typeof e)return e;if(!1===e)return null;if(!0===e&&!r)throw new Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return r??null}({queryString:e,groupId:r});return[(0,c.aZ)(t),(0,a.useCallback)(e=>{if(!t)return;const r=new URLSearchParams(n.location.search);r.set(t,e),n.replace({...n.location,search:r.toString()})},[t,n])]}function g(e){const{defaultValue:r,queryString:n=!1,groupId:t}=e,s=p(e),[l,o]=(0,a.useState)(()=>function({defaultValue:e,tabValues:r}){if(0===r.length)throw new Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(e){if(!m({value:e,tabValues:r}))throw new Error(`Docusaurus error: The <Tabs> has a defaultValue "${e}" but none of its children has the corresponding value. Available values are: ${r.map(e=>e.value).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return e}const n=r.find(e=>e.default)??r[0];if(!n)throw new Error("Unexpected error: 0 tabValues");return n.value}({defaultValue:r,tabValues:s})),[c,u]=b({queryString:n,groupId:t}),[h,g]=function({groupId:e}){const r=function(e){return e?`docusaurus.tab.${e}`:null}(e),[n,t]=(0,d.Dv)(r);return[n,(0,a.useCallback)(e=>{r&&t.set(e)},[r,t])]}({groupId:t}),f=(()=>{const e=c??h;return m({value:e,tabValues:s})?e:null})();(0,i.A)(()=>{f&&o(f)},[f]);return{selectedValue:l,selectValue:(0,a.useCallback)(e=>{if(!m({value:e,tabValues:s}))throw new Error(`Can't select invalid tab value=${e}`);o(e),u(e),g(e)},[u,g,s]),tabValues:s}}var f=n(2303);const y={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var x=n(4848);function j({className:e,block:r,selectedValue:n,selectValue:a,tabValues:s}){const o=[],{blockElementScrollPositionUntilNextRender:i}=(0,l.a_)(),c=e=>{const r=e.currentTarget,t=o.indexOf(r),l=s[t].value;l!==n&&(i(r),a(l))},u=e=>{let r=null;switch(e.key){case"Enter":c(e);break;case"ArrowRight":{const n=o.indexOf(e.currentTarget)+1;r=o[n]??o[0];break}case"ArrowLeft":{const n=o.indexOf(e.currentTarget)-1;r=o[n]??o[o.length-1];break}}r?.focus()};return(0,x.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,t.A)("tabs",{"tabs--block":r},e),children:s.map(({value:e,label:r,attributes:a})=>(0,x.jsx)("li",{role:"tab",tabIndex:n===e?0:-1,"aria-selected":n===e,ref:e=>{o.push(e)},onKeyDown:u,onClick:c,...a,className:(0,t.A)("tabs__item",y.tabItem,a?.className,{"tabs__item--active":n===e}),children:r??e},e))})}function v({lazy:e,children:r,selectedValue:n}){const s=(Array.isArray(r)?r:[r]).filter(Boolean);if(e){const e=s.find(e=>e.props.value===n);return e?(0,a.cloneElement)(e,{className:(0,t.A)("margin-top--md",e.props.className)}):null}return(0,x.jsx)("div",{className:"margin-top--md",children:s.map((e,r)=>(0,a.cloneElement)(e,{key:r,hidden:e.props.value!==n}))})}function k(e){const r=g(e);return(0,x.jsxs)("div",{className:(0,t.A)(s.G.tabs.container,"tabs-container",y.tabList),
1children:[(0,x.jsx)(j,{...r,...e}),(0,x.jsx)(v,{...r,...e})]})}function T(e){const r=(0,f.A)();return(0,x.jsx)(k,{...e,children:h(e.children)},String(r))}},8453:(e,r,n)=>{n.d(r,{R:()=>l,x:()=>o});var a=n(6540);const t={},s=a.createContext(t);function l(e){const r=a.useContext(s);return a.useMemo(function(){return"function"==typeof e?e(r):{...r,...e}},[r,e])}function o(e){let r;return r=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:l(e.components),a.createElement(s.Provider,{value:r},e.children)}},9365:(e,r,n)=>{n.d(r,{A:()=>l});n(6540);var a=n(4164);const t={tabItem:"tabItem_Ymn6"};var s=n(4848);function l({children:e,hidden:r,className:n}){return(0,s.jsx)("div",{role:"tabpanel",className:(0,a.A)(t.tabItem,n),hidden:r,children:e})}},9419:(e,r,n)=>{n.r(r),n.d(r,{assets:()=>u,contentTitle:()=>c,default:()=>p,frontMatter:()=>i,metadata:()=>a,toc:()=>d});const a=JSON.parse('{"id":"temporary-url-bundle/index","title":"rekalogika/temporary-url-bundle","description":"Symfony bundle for creating temporary URLs to your resources. You provide the","source":"@site/docs/temporary-url-bundle/index.md","sourceDirName":"temporary-url-bundle","slug":"/temporary-url-bundle/","permalink":"/temporary-url-bundle/","draft":false,"unlisted":false,"editUrl":"https://github.com/rekalogika/rekalogika-docs/edit/main/docs/temporary-url-bundle/index.md","tags":[],"version":"current","frontMatter":{"title":"rekalogika/temporary-url-bundle"},"sidebar":"docs","previous":{"title":"Troubleshooting","permalink":"/rekapager/troubleshooting"}}');var t=n(4848),s=n(8453),l=n(1470),o=n(9365);const i={title:"rekalogika/temporary-url-bundle"},c=void 0,u={},d=[{value:"Installation",id:"installation",level:2},{value:"Creating a Resource Class",id:"creating-a-resource-class",level:2},{value:"Creating a Resource Server",id:"creating-a-resource-server",level:2},{value:"Generating a Temporary URL",id:"generating-a-temporary-url",level:2},{value:"In Twig Templates",id:"in-twig-templates",level:2},{value:"Dealing With Unserializable Resources",id:"dealing-with-unserializable-resources",level:2},{value:"License",id:"license",level:2},{value:"Contributing",id:"contributing",level:2}];function h(e){const r={a:"a",admonition:"admonition",code:"code",h2:"h2",li:"li",p:"p",pre:"pre",ul:"ul",...(0,s.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(r.p,{children:"Symfony bundle for creating temporary URLs to your resources. You provide the\nresource in a plain PHP object, and a service to turn it into a HTTP response.\nThe framework handles the rest."}),"\n",(0,t.jsx)(r.h2,{id:"installation",children:"Installation"}),"\n",(0,t.jsxs)(r.p,{children:["Make sure Composer is installed globally, as explained in the\n",(0,t.jsx)(r.a,{href:"https://getcomposer.org/doc/00-intro.md",children:"installation chapter"}),"\nof the Composer documentation."]}),"\n",(0,t.jsxs)(l.A,{children:[(0,t.jsxs)(o.A,{value:"flex",label:"With Symfony Flex",children:[(0,t.jsx)(r.p,{children:"Open a command console, enter your project directory, and execute:"}),(0,t.jsx)(r.pre,{children:(0,t.jsx)(r.code,{className:"language-bash",children:"composer config extra.symfony.allow-contrib true\ncomposer require rek
1alogika/temporary-url-bundle\n"})})]}),(0,t.jsxs)(o.A,{value:"noflex",label:"Without Symfony Flex",children:[(0,t.jsx)(r.p,{children:"Step 1: Download the Bundle"}),(0,t.jsx)(r.p,{children:"Open a command console, enter your project directory, and execute the\nfollowing command to download the latest stable version of this bundle:"}),(0,t.jsx)(r.pre,{children:(0,t.jsx)(r.code,{className:"language-bash",children:"composer require rekalogika/temporary-url-bundle\n"})}),(0,t.jsx)(r.p,{children:"Step 2: Enable the Bundle"}),(0,t.jsxs)(r.p,{children:["Then, enable the bundle by adding it to the list of registered bundles\nin the ",(0,t.jsx)(r.code,{children:"config/bundles.php"})," file of your project:"]}),(0,t.jsx)(r.pre,{children:(0,t.jsx)(r.code,{className:"language-php",metastring:'title="config/bundles.php"',children:"return [\n // ...\n Rekalogika\\TemporaryUrl\\RekalogikaTemporaryUrlBundle::class => ['all' => true],\n];\n"})}),(0,t.jsx)(r.p,{children:"Step 3: Configure the route"}),(0,t.jsxs)(r.p,{children:["Add the route in ",(0,t.jsx)(r.code,{children:"config/routes/rekalogika_temporary_url.yaml"}),"."]}),(0,t.jsx)(r.pre,{children:(0,t.jsx)(r.code,{className:"language-yaml",metastring:'title="config/routes/rekalogika_temporary_url.yaml"',children:"rekalogika_temporary_url:\n resource: '@RekalogikaTemporaryUrlBundle/config/routes.xml'\n prefix: /_temporary\n"})}),(0,t.jsx)(r.admonition,{type:"note",children:(0,t.jsx)(r.p,{children:"You may change the prefix if you like."})}),(0,t.jsx)(r.p,{children:"Step 4: Add the Stimulus controller in the configuration"}),(0,t.jsx)(r.pre,{children:(0,t.jsx)(r.code,{className:"language-json",metastring:'title="assets/controllers.json"',children:'{\n "controllers": {\n "@rekalogika/temporary-url-bundle": {\n "autoexpire": {\n "enabled": true,\n "fetch": "eager",\n "autoimport": []\n }\n }\n }\n}\n'})})]})]}),"\n",(0,t.jsx)(r.p,{children:"If you're using WebpackEncore, install your assets and restart Encore (not\nneeded if you're using AssetMapper):"}),"\n",(0,t.jsxs)(l.A,{children:[(0,t.jsx)(o.A,{value:"yarn",label:"Yarn",children:(0,t.jsx)(r.pre,{children:(0,t.jsx)(r.code,{className:"language-bash",children:"yarn install --force\nyarn watch\n"})})}),(0,t.jsx)(o.A,{value:"npm",label:"NPM",children:(0,t.jsx)(r.pre,{children:(0,t.jsx)(r.code,{className:"language-bash",children:"npm install --force\nnpm run watch\n"})})})]}),"\n",(0,t.jsx)(r.h2,{id:"creating-a-resource-class",children:"Creating a Resource Class"}),"\n",(0,t.jsx)(r.p,{children:"Create a class that describes your resource. There is no particular requirement\nfor this class, except that it must be serializable."}),"\n",(0,t.jsx)(r.pre,{children:(0,t.jsx)(r.code,{className:"language-php",children:"class MyData\n{\n public function __construct(private string $name)\n {\n }\n\n public function getName(): string\n {\n return $this->name;\n }\n}\n"})}),"\n",(0,t.jsx)(r.admonition,{title:"Protip",type:"tip",children:(0,t.jsx)(r.p,{children:"You can reuse your existing event, message, DTO, value objects, or\nother similar classes for this purpose."})}),"\n",(0,t.jsx)(r.h2,{id:"creating-a-resource-server",children:"Creating a Resource Server"}),"\n",(0,t.jsxs)(r.p,{children:["Then create a server class or method that transforms the resource into an HTTP\nresponse. Use the ",(0,t.jsx)(r.code,{children:"AsTemporaryUrlServer"})," attribute to mark the method as a\ntemporary URL server. If the attribute is attached to the class, then the method\nis assumed to be ",(0,t.jsx)(r.code,{children:"__invoke()"}),". The method must accept the resource as its first\nargument, and return a ",(0,t.jsx)(r.code,{children:"Response"})," object."]}),"\n",(0,t.jsx)(r.pre,{children:(0,t.jsx)(r.code,{className:"language-php",children:"use Rekalogika\\TemporaryUrl\\Attribute\\AsTemporaryUrlServer;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\nclass MyDataServer\n{\n #[AsTemporaryUrlServer]\n public function respond(MyData $data): Response\n {\n return new Response('My name is ' . $data->getName());\n }\n}\n"})}),"\n",(0,t.jsx)(r.h2,{id:"generating-a-temporary-url",children:"Generating a Temporary URL"}),"\n",(0,t.jsxs)(r.p,{children:["To generate a temporary URL, use the ",(0,t.jsx)(r.code,{children:"TemporaryUrlGeneratorInterface"})," service."]}),"\n",(0,t.jsx)(r.pre,{children:(0,t.jsx)(r.code,{className:"language-php",children:"use Rekalogika\\TemporaryUrl\\TemporaryUrlGeneratorInterface;\n\n/** @var TemporaryUrlGeneratorInterface $temporaryUrlGenerator */\n\n$resource = new MyData('123');\n$url = $temporaryUrlGenerator->generateUrl($resource);\n"})}),"\n",(0,t.jsxs)(r.p,{children:["The ",(0,t.jsx)(r.code,{children:"TemporaryUrlGeneratorInterface::generateUrl()"})," offers additional options:"]}),"\n",(0,t.jsxs)(r.ul,{children:["\n",(0,t.jsxs)(r.li,{children:[(0,t.jsx)(r.code,{children:"$ttl"})," (",(0,t.jsx)(r.code,{children:"int"})," or ",(0,t.jsx)(r.code,{children:"DateInterval"}),"): The time-to-live of the URL. Defaults to 30\nminutes."]}),"\n",(0,t.jsxs)(r.li,{children:[(0,t.jsx)(r.code,{children:"$pinSession"})," (",(0,t.jsx)(r.code,{children:"bool"}),"): Whether to pin the URL to the session. Pinned URLs can\nonly be accessed by the same user that generated them. Defaults to ",(0,t.jsx)(r.code,{children:"false"}),"."]}),"\n",(0,t.jsxs)(r.li,{children:[(0,t.jsx)(r.code,{children:"$referenceType"})," (",(0,t.jsx)(r.code,{children:"int"}),"): The type of reference to be generated (one of the\n",(0,t.jsx)(r.code,{children:"UrlGeneratorInterface::ABSOLUTE_*"})," constants). Defaults to\n",(0,t.jsx)(r.code,{children:"UrlGeneratorInterface::ABSOLUTE_PATH"}),"."]}),"\n"]}),"\n",(0,t.jsx)(r.h2,{id:"in-twig-templates",children:"In Twig Templates"}),"\n",(0,t.jsxs)(r.p,{children:["In a Twig template, you can use the filter ",(0,t.jsx)(r.code,{children:"temporary_url"})," to generate a\ntemporary URL. Use the function ",(0,t.jsx)(r.code,{children:"temporary_url_autoexpire"})," on links to make them\nunclickable after the URL expires."]}),"\n",(0,t.jsx)(r.pre,{children:(0,t.jsx)(r.code,{className:"language-twig",children:'{# my_data here is a resource object #}\n<a href="{{ my_data|temporary_url }}
1" {{ temporary_url_autoexpire() }}>\n Click here to download my data\n</a>\n'})}),"\n",(0,t.jsxs)(r.p,{children:["The filter accepts the same options as the ",(0,t.jsx)(r.code,{children:"generateUrl()"})," method above."]}),"\n",(0,t.jsx)(r.h2,{id:"dealing-with-unserializable-resources",children:"Dealing With Unserializable Resources"}),"\n",(0,t.jsx)(r.p,{children:"If your resource is not serializable, you can create a resource transformer\nmethod that converts your resource into an intermediate serializable object."}),"\n",(0,t.jsx)(r.pre,{children:(0,t.jsx)(r.code,{className:"language-php",children:"use Rekalogika\\TemporaryUrl\\Attribute\\AsTemporaryUrlResourceTransformer;\nuse Rekalogika\\TemporaryUrl\\Attribute\\AsTemporaryUrlServer;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\nclass MyDataServer\n{\n /**\n * This method transforms the resource into a serializable object.\n */\n #[AsTemporaryUrlResourceTransformer]\n public function transform(MyUnserializableData $data): MySerializableData\n {\n return new MySerializableData($data);\n }\n\n ./**\n * This uses the transformed data and send it to the client.\n */\n #[AsTemporaryUrlServer]\n public function respond(MySerializableData $data): Response\n {\n return new Response('My name is ' . $data->getName());\n }\n}\n"})}),"\n",(0,t.jsxs)(r.p,{children:["Using the above example, you will be able to generate a temporary URL to\n",(0,t.jsx)(r.code,{children:"MyUnserializableData"}),". The framework will automatically transform\n",(0,t.jsx)(r.code,{children:"MyUnserializableData"})," to ",(0,t.jsx)(r.code,{children:"MySerializableData"})," behind the scenes."]}),"\n",(0,t.jsx)(r.h2,{id:"license",children:"License"}),"\n",(0,t.jsx)(r.p,{children:"MIT"}),"\n",(0,t.jsx)(r.h2,{id:"contributing",children:"Contributing"}),"\n",(0,t.jsxs)(r.p,{children:["Issues and pull requests should be filed in the GitHub repository\n",(0,t.jsx)(r.a,{href:"https://github.com/rekalogika/temporary-url-bundle",children:"rekalogika/temporary-url-bundle"}),"."]})]})}function p(e={}){const{wrapper:r}={...(0,s.R)(),...e.components};return r?(0,t.jsx)(r,{...e,children:(0,t.jsx)(h,{...e})}):h(e)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.